diff --git a/core/core.services.yml b/core/core.services.yml
index 328acd8..65e0a40 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -32,6 +32,17 @@ services:
     class: Drupal\Core\Cache\TimeZoneCacheContext
     tags:
       - { name: cache.context}
+  cache_tags.invalidator:
+    class: Drupal\Core\Cache\CacheTagsInvalidator
+    calls:
+      - [setContainer, ['@service_container']]
+    tags:
+      - { name: service_collector, call: addInvalidator }
+  cache_tags.checksum:
+    class: Drupal\Core\Cache\DatabaseCacheTagsChecksum
+    arguments: ['@database']
+    tags:
+      - { name: cache_tags.invalidator}
   cache.backend.chainedfast:
     class: Drupal\Core\Cache\ChainedFastBackendFactory
     arguments: ['@settings']
@@ -39,12 +50,13 @@ services:
       - [setContainer, ['@service_container']]
   cache.backend.database:
     class: Drupal\Core\Cache\DatabaseBackendFactory
-    arguments: ['@database']
+    arguments: ['@database', '@cache_tags.checksum']
   cache.backend.apcu:
     class: Drupal\Core\Cache\ApcuBackendFactory
-    arguments: ['@app.root']
+    arguments: ['@app.root', '@cache_tags.checksum']
   cache.backend.php:
     class: Drupal\Core\Cache\PhpBackendFactory
+    arguments: ['@cache_tags.checksum']
   cache.bootstrap:
     class: Drupal\Core\Cache\CacheBackendInterface
     tags:
diff --git a/core/lib/Drupal/Core/Cache/ApcuBackend.php b/core/lib/Drupal/Core/Cache/ApcuBackend.php
index 50fb0fd..e406d0f 100644
--- a/core/lib/Drupal/Core/Cache/ApcuBackend.php
+++ b/core/lib/Drupal/Core/Cache/ApcuBackend.php
@@ -10,7 +10,7 @@
 /**
  * Stores cache items in the Alternative PHP Cache User Cache (APCu).
  */
-class ApcuBackend implements CacheBackendInterface {
+class ApcuBackend implements CacheBackendInterface, CacheTagsInvalidatorInterface {
 
   /**
    * The name of the cache bin to use.
@@ -45,20 +45,18 @@ class ApcuBackend implements CacheBackendInterface {
   protected $invalidationsTagsPrefix;
 
   /**
-   * Prefix for keys holding invalidation cache tags.
+   * The cache tags checksum provider.
    *
-   * Includes the site-specific prefix in $sitePrefix.
-   *
-   * @var string
+   * @var \Drupal\Core\Cache\CacheTagsChecksumInterface
    */
-  protected $deletionsTagsPrefix;
+  protected $checksumProvider;
 
   /**
    * A static cache of all tags checked during the request.
    *
    * @var array
    */
-  protected static $tagCache = array('deletions' => array(), 'invalidations' => array());
+  protected static $tagCache = array();
 
   /**
    * Constructs a new ApcuBackend instance.
@@ -67,13 +65,15 @@ class ApcuBackend implements CacheBackendInterface {
    *   The name of the cache bin.
    * @param string $site_prefix
    *   The prefix to use for all keys in the storage that belong to this site.
+   * @param \Drupal\Core\Cache\CacheTagsChecksumInterface $checksum_provider
+   *   The cache tags checksum provider.
    */
-  public function __construct($bin, $site_prefix) {
+  public function __construct($bin, $site_prefix, CacheTagsChecksumInterface $checksum_provider) {
     $this->bin = $bin;
     $this->sitePrefix = $site_prefix;
+    $this->checksumProvider = $checksum_provider;
     $this->binPrefix = $this->sitePrefix . '::' . $this->bin . '::';
     $this->invalidationsTagsPrefix = $this->sitePrefix . '::itags::';
-    $this->deletionsTagsPrefix = $this->sitePrefix . '::dtags::';
   }
 
   /**
@@ -163,18 +163,12 @@ protected function prepareItem($cache, $allow_invalid) {
     }
 
     $cache->tags = $cache->tags ? explode(' ', $cache->tags) : array();
-    $checksum = $this->checksumTags($cache->tags);
-
-    // Check if deleteTags() has been called with any of the entry's tags.
-    if ($cache->checksum_deletions != $checksum['deletions']) {
-      return FALSE;
-    }
 
     // Check expire time.
     $cache->valid = $cache->expire == Cache::PERMANENT || $cache->expire >= REQUEST_TIME;
 
     // Check if invalidateTags() has been called with any of the entry's tags.
-    if ($cache->checksum_invalidations != $checksum['invalidations']) {
+    if (!$this->checksumProvider->isValid($cache->checksum, $cache->tags)) {
       $cache->valid = FALSE;
     }
 
@@ -189,16 +183,17 @@ protected function prepareItem($cache, $allow_invalid) {
    * {@inheritdoc}
    */
   public function set($cid, $data, $expire = CacheBackendInterface::CACHE_PERMANENT, array $tags = array()) {
-    Cache::validateTags($tags);
-    $tags = array_unique($tags);
+    if ($tags) {
+      $tags = array_unique($tags);
+      Cache::validateTags($tags);
+    }
+
     $cache = new \stdClass();
     $cache->cid = $cid;
     $cache->created = round(microtime(TRUE), 3);
     $cache->expire = $expire;
     $cache->tags = implode(' ', $tags);
-    $checksum = $this->checksumTags($tags);
-    $cache->checksum_invalidations = $checksum['invalidations'];
-    $cache->checksum_deletions = $checksum['deletions'];
+    $cache->checksum = $this->checksumProvider->getCurrentChecksum($tags);
     // APC serializes/unserializes any structure itself.
     $cache->serialized = 0;
     $cache->data = $data;
@@ -286,18 +281,6 @@ public function invalidateAll() {
   /**
    * {@inheritdoc}
    */
-  public function deleteTags(array $tags) {
-    foreach ($tags as $tag) {
-      apc_inc($this->deletionsTagsPrefix . $tag, 1, $success);
-      if (!$success) {
-        apc_store($this->deletionsTagsPrefix . $tag, 1);
-      }
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function invalidateTags(array $tags) {
     foreach ($tags as $tag) {
       apc_inc($this->invalidationsTagsPrefix . $tag, 1, $success);
@@ -307,41 +290,4 @@ public function invalidateTags(array $tags) {
     }
   }
 
-  /**
-   * Returns the sum total of validations for a given set of tags.
-   *
-   * @param array $tags
-   *   Associative array of tags.
-   *
-   * @return int
-   *   Sum of all invalidations.
-   */
-  protected function checksumTags(array $tags) {
-    $checksum = array('invalidations' => 0, 'deletions' => 0);
-    $query_tags = array('invalidations' => array(), 'deletions' => array());
-
-    foreach ($tags as $tag) {
-      foreach (array('deletions', 'invalidations') as $type) {
-        if (isset(static::$tagCache[$type][$tag])) {
-          $checksum[$type] += static::$tagCache[$type][$tag];
-        }
-        else {
-          $query_tags[$type][] = $this->{$type . 'TagsPrefix'} . $tag;
-        }
-      }
-    }
-
-    foreach (array('deletions', 'invalidations') as $type) {
-      if ($query_tags[$type]) {
-        $result = apc_fetch($query_tags[$type]);
-        if ($result) {
-          static::$tagCache[$type] = array_merge(static::$tagCache[$type], $result);
-          $checksum[$type] += array_sum($result);
-        }
-      }
-    }
-
-    return $checksum;
-  }
-
 }
diff --git a/core/lib/Drupal/Core/Cache/ApcuBackendFactory.php b/core/lib/Drupal/Core/Cache/ApcuBackendFactory.php
index ffe5993..bbc4e3d 100644
--- a/core/lib/Drupal/Core/Cache/ApcuBackendFactory.php
+++ b/core/lib/Drupal/Core/Cache/ApcuBackendFactory.php
@@ -19,13 +19,23 @@ class ApcuBackendFactory implements CacheFactoryInterface {
   protected $sitePrefix;
 
   /**
+   * The cache tags checksum provider.
+   *
+   * @var \Drupal\Core\Cache\CacheTagsChecksumInterface
+   */
+  protected $checksumProvider;
+
+  /**
    * Constructs an ApcuBackendFactory object.
    *
    * @param string $root
    *   The app root.
+   * @param \Drupal\Core\Cache\CacheTagsChecksumInterface $checksum_provider
+   *   The cache tags checksum provider.
    */
-  public function __construct($root) {
+  public function __construct($root, CacheTagsChecksumInterface $checksum_provider) {
     $this->sitePrefix = Crypt::hashBase64($root . '/' . conf_path());
+    $this->checksumProvider = $checksum_provider;
   }
 
   /**
@@ -38,7 +48,7 @@ public function __construct($root) {
    *   The cache backend object for the specified cache bin.
    */
   public function get($bin) {
-    return new ApcuBackend($bin, $this->sitePrefix);
+    return new ApcuBackend($bin, $this->sitePrefix, $this->checksumProvider);
   }
 
 }
diff --git a/core/lib/Drupal/Core/Cache/BackendChain.php b/core/lib/Drupal/Core/Cache/BackendChain.php
index 0cea142..f7681d9 100644
--- a/core/lib/Drupal/Core/Cache/BackendChain.php
+++ b/core/lib/Drupal/Core/Cache/BackendChain.php
@@ -23,7 +23,7 @@
  * @ingroup cache
  */
 
-class BackendChain implements CacheBackendInterface {
+class BackendChain implements CacheBackendInterface, CacheTagsInvalidatorInterface {
 
   /**
    * Ordered list of CacheBackendInterface instances.
@@ -159,15 +159,6 @@ public function deleteMultiple(array $cids) {
   }
 
   /**
-   * Implements Drupal\Core\Cache\CacheBackendInterface::deleteTags().
-   */
-  public function deleteTags(array $tags) {
-    foreach ($this->backends as $backend) {
-      $backend->deleteTags($tags);
-    }
-  }
-
-  /**
    * Implements Drupal\Core\Cache\CacheBackendInterface::deleteAll().
    */
   public function deleteAll() {
@@ -199,7 +190,9 @@ public function invalidateMultiple(array $cids) {
    */
   public function invalidateTags(array $tags) {
     foreach ($this->backends as $backend) {
-      $backend->invalidateTags($tags);
+      if ($backend instanceof CacheTagsInvalidatorInterface) {
+        $backend->invalidateTags($tags);
+      }
     }
   }
 
diff --git a/core/lib/Drupal/Core/Cache/Cache.php b/core/lib/Drupal/Core/Cache/Cache.php
index ddf444e..4ce3610 100644
--- a/core/lib/Drupal/Core/Cache/Cache.php
+++ b/core/lib/Drupal/Core/Cache/Cache.php
@@ -94,43 +94,13 @@ public static function buildTags($prefix, array $suffixes) {
   }
 
   /**
-   * Deletes items from all bins with any of the specified tags.
-   *
-   * Many sites have more than one active cache backend, and each backend may
-   * use a different strategy for storing tags against cache items, and
-   * deleting cache items associated with a given tag.
-   *
-   * When deleting a given list of tags, we iterate over each cache backend, and
-   * and call deleteTags() on each.
-   *
-   * @param string[] $tags
-   *   The list of tags to delete cache items for.
-   */
-  public static function deleteTags(array $tags) {
-    static::validateTags($tags);
-    foreach (static::getBins() as $cache_backend) {
-      $cache_backend->deleteTags($tags);
-    }
-  }
-
-  /**
    * Marks cache items from all bins with any of the specified tags as invalid.
    *
-   * Many sites have more than one active cache backend, and each backend my use
-   * a different strategy for storing tags against cache items, and invalidating
-   * cache items associated with a given tag.
-   *
-   * When invalidating a given list of tags, we iterate over each cache backend,
-   * and call invalidateTags() on each.
-   *
    * @param string[] $tags
    *   The list of tags to invalidate cache items for.
    */
   public static function invalidateTags(array $tags) {
-    static::validateTags($tags);
-    foreach (static::getBins() as $cache_backend) {
-      $cache_backend->invalidateTags($tags);
-    }
+    \Drupal::service('cache_tags.invalidator')->invalidateTags($tags);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Cache/CacheBackendInterface.php b/core/lib/Drupal/Core/Cache/CacheBackendInterface.php
index 39c8c26..e5aa85d 100644
--- a/core/lib/Drupal/Core/Cache/CacheBackendInterface.php
+++ b/core/lib/Drupal/Core/Cache/CacheBackendInterface.php
@@ -136,7 +136,6 @@ public function setMultiple(array $items);
    *
    * @see \Drupal\Core\Cache\CacheBackendInterface::invalidate()
    * @see \Drupal\Core\Cache\CacheBackendInterface::deleteMultiple()
-   * @see \Drupal\Core\Cache\CacheBackendInterface::deleteTags()
    * @see \Drupal\Core\Cache\CacheBackendInterface::deleteAll()
    */
   public function delete($cid);
@@ -155,39 +154,16 @@ public function delete($cid);
    *
    * @see \Drupal\Core\Cache\CacheBackendInterface::invalidateMultiple()
    * @see \Drupal\Core\Cache\CacheBackendInterface::delete()
-   * @see \Drupal\Core\Cache\CacheBackendInterface::deleteTags()
    * @see \Drupal\Core\Cache\CacheBackendInterface::deleteAll()
    */
   public function deleteMultiple(array $cids);
 
   /**
-   * Deletes items with any of the specified tags.
-   *
-   * If the cache items are being deleted because they are no longer "fresh",
-   * you may consider using invalidateTags() instead. This allows callers to
-   * retrieve the invalid items by calling get() with $allow_invalid set to TRUE.
-   * In some cases an invalid item may be acceptable rather than having to
-   * rebuild the cache.
-   *
-   * @param array $tags
-   *   Associative array of tags, in the same format that is passed to
-   *   CacheBackendInterface::set().
-   *
-   * @see \Drupal\Core\Cache\CacheBackendInterface::set()
-   * @see \Drupal\Core\Cache\CacheBackendInterface::invalidateTags()
-   * @see \Drupal\Core\Cache\CacheBackendInterface::delete()
-   * @see \Drupal\Core\Cache\CacheBackendInterface::deleteMultiple()
-   * @see \Drupal\Core\Cache\CacheBackendInterface::deleteAll()
-   */
-  public function deleteTags(array $tags);
-
-  /**
    * Deletes all cache items in a bin.
    *
    * @see \Drupal\Core\Cache\CacheBackendInterface::invalidateAll()
    * @see \Drupal\Core\Cache\CacheBackendInterface::delete()
    * @see \Drupal\Core\Cache\CacheBackendInterface::deleteMultiple()
-   * @see \Drupal\Core\Cache\CacheBackendInterface::deleteTags()
    */
   public function deleteAll();
 
@@ -224,21 +200,6 @@ public function invalidate($cid);
   public function invalidateMultiple(array $cids);
 
   /**
-   * Marks cache items with any of the specified tags as invalid.
-   *
-   * @param array $tags
-   *   Associative array of tags, in the same format that is passed to
-   *   CacheBackendInterface::set().
-   *
-   * @see \Drupal\Core\Cache\CacheBackendInterface::set()
-   * @see \Drupal\Core\Cache\CacheBackendInterface::deleteTags()
-   * @see \Drupal\Core\Cache\CacheBackendInterface::invalidate()
-   * @see \Drupal\Core\Cache\CacheBackendInterface::invalidateMultiple()
-   * @see \Drupal\Core\Cache\CacheBackendInterface::invalidateAll()
-   */
-  public function invalidateTags(array $tags);
-
-  /**
    * Marks all cache items as invalid.
    *
    * Invalid items may be returned in later calls to get(), if the $allow_invalid
diff --git a/core/lib/Drupal/Core/Cache/CacheCollector.php b/core/lib/Drupal/Core/Cache/CacheCollector.php
index d80da7d..a6d8ab5 100644
--- a/core/lib/Drupal/Core/Cache/CacheCollector.php
+++ b/core/lib/Drupal/Core/Cache/CacheCollector.php
@@ -280,7 +280,7 @@ public function reset() {
   public function clear() {
     $this->reset();
     if ($this->tags) {
-      Cache::deleteTags($this->tags);
+      Cache::invalidateTags($this->tags);
     }
     else {
       $this->cache->delete($this->getCid());
diff --git a/core/lib/Drupal/Core/Cache/CacheTagsChecksumInterface.php b/core/lib/Drupal/Core/Cache/CacheTagsChecksumInterface.php
new file mode 100644
index 0000000..265f624
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/CacheTagsChecksumInterface.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Cache\CacheTagsChecksumInterface.
+ */
+
+namespace Drupal\Core\Cache;
+
+/**
+ * Provides checksums for cache tag invalidations.
+ *
+ * Cache backends can use this to check if any cache tag invalidations
+ *
+ * @ingroup cache
+ */
+interface CacheTagsChecksumInterface {
+
+  /**
+   * Returns the sum total of validations for a given set of tags.
+   *
+   * Called by a backend when storing a cache item.
+   *
+   * @param string[] $tags
+   *   Array of cache tags.
+   *
+   * @return int
+   *   Cache tag invalidations checksum.
+   */
+  public function getCurrentChecksum(array $tags);
+
+  /**
+   * Returns whether the checksum is valid for the given cache tags.
+   *
+   * Used when retrieving a cache item in a cache backend, to verify that no
+   * cache tag based invalidation happened.
+   *
+   * @param int $checksum
+   *   The checksum that was stored together with the cache item.
+   * @param string[] $tags
+   *   The cache tags that were stored together with the cache item.
+   *
+   * @return bool
+   *   FALSE if cache tag invalidations happened for the passed in tags since
+   *   the cache item was stored, TRUE otherwise.
+   */
+  public function isValid($checksum, array $tags);
+
+  /**
+   * Reset statically cached tags.
+   *
+   * This is only used by tests.
+   */
+  public function reset();
+}
diff --git a/core/lib/Drupal/Core/Cache/CacheTagsInvalidator.php b/core/lib/Drupal/Core/Cache/CacheTagsInvalidator.php
new file mode 100644
index 0000000..636ce77
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/CacheTagsInvalidator.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Cache\CacheTagsInvalidator.
+ */
+
+namespace Drupal\Core\Cache;
+
+use Symfony\Component\DependencyInjection\ContainerAwareTrait;
+
+/**
+ * Passes cache tag events to classes that wish to respond to them.
+ */
+class CacheTagsInvalidator implements CacheTagsInvalidatorInterface {
+
+  use ContainerAwareTrait;
+
+  /**
+   * Holds an array of cache tags invalidators.
+   *
+   * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface[]
+   */
+  protected $invalidators = array();
+
+  /**
+   * {@inheritdoc}
+   */
+  public function invalidateTags(array $tags) {
+    // Validate the tags.
+    Cache::validateTags($tags);
+
+    // Notify all added cache tags invalidators.
+    foreach ($this->invalidators as $invalidator) {
+      $invalidator->invalidateTags($tags);
+    }
+
+    // Additionally, notify each cache bin if it implements the service.
+    foreach ($this->getInvalidatorCacheBins() as $bin) {
+      $bin->invalidateTags($tags);
+    }
+  }
+
+  /**
+   * Adds a cache tags invalidator.
+   *
+   * @param \Drupal\Core\Cache\CacheTagsInvalidatorInterface $invalidator
+   *   A cache invalidator.
+   */
+  public function addInvalidator(CacheTagsInvalidatorInterface $invalidator) {
+    $this->invalidators[] = $invalidator;
+  }
+
+  /**
+   * Returns all cache bins that need to be notified about invalidations.
+   *
+   * @return \Drupal\Core\Cache\CacheTagsInvalidatorInterface[]
+   *   An array of cache backend objects that implement the invalidator
+   *   interface, keyed by their cache bin.
+   */
+  protected function getInvalidatorCacheBins() {
+    $bins = array();
+    foreach ($this->container->getParameter('cache_bins') as $service_id => $bin) {
+      $service = $this->container->get($service_id);
+      if ($service instanceof CacheTagsInvalidatorInterface) {
+        $bins[$bin] = $service;
+      }
+    }
+    return $bins;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Cache/CacheTagsInvalidatorInterface.php b/core/lib/Drupal/Core/Cache/CacheTagsInvalidatorInterface.php
new file mode 100644
index 0000000..4db0a96
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/CacheTagsInvalidatorInterface.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Cache\CacheTagsInvalidatorInterface
+ */
+
+namespace Drupal\Core\Cache;
+
+/**
+ * Defines required methods for classes wanting to handle cache tag changes.
+ *
+ * @ingroup cache
+ */
+interface CacheTagsInvalidatorInterface {
+
+  /**
+   * Marks cache items with any of the specified tags as invalid.
+   *
+   * @param string[] $tags
+   *   The list of tags for which to invalidate cache items.
+   */
+  public function invalidateTags(array $tags);
+
+}
diff --git a/core/lib/Drupal/Core/Cache/ChainedFastBackend.php b/core/lib/Drupal/Core/Cache/ChainedFastBackend.php
index 559cff7..ce5ddd2 100644
--- a/core/lib/Drupal/Core/Cache/ChainedFastBackend.php
+++ b/core/lib/Drupal/Core/Cache/ChainedFastBackend.php
@@ -41,7 +41,7 @@
  *
  * @ingroup cache
  */
-class ChainedFastBackend implements CacheBackendInterface {
+class ChainedFastBackend implements CacheBackendInterface, CacheTagsInvalidatorInterface {
 
   /**
    * Cache key prefix for the bin-specific entry to track the last write.
@@ -213,14 +213,6 @@ public function deleteMultiple(array $cids) {
   /**
    * {@inheritdoc}
    */
-  public function deleteTags(array $tags) {
-    $this->markAsOutdated();
-    $this->consistentBackend->deleteTags($tags);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function deleteAll() {
     $this->consistentBackend->deleteAll();
     $this->markAsOutdated();
@@ -245,7 +237,9 @@ public function invalidateMultiple(array $cids) {
    * {@inheritdoc}
    */
   public function invalidateTags(array $tags) {
-    $this->consistentBackend->invalidateTags($tags);
+    if ($this->consistentBackend instanceof CacheTagsInvalidatorInterface) {
+      $this->consistentBackend->invalidateTags($tags);
+    }
     $this->markAsOutdated();
   }
 
diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackend.php b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
index 813c00a..044c910 100644
--- a/core/lib/Drupal/Core/Cache/DatabaseBackend.php
+++ b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
@@ -35,19 +35,29 @@ class DatabaseBackend implements CacheBackendInterface {
   protected $connection;
 
   /**
+   * The cache tags checksum provider.
+   *
+   * @var \Drupal\Core\Cache\CacheTagsChecksumInterface
+   */
+  protected $checksumProvider;
+
+  /**
    * Constructs a DatabaseBackend object.
    *
    * @param \Drupal\Core\Database\Connection $connection
    *   The database connection.
+   * @param \Drupal\Core\Cache\CacheTagsChecksumInterface $checksum_provider
+   *   The cache tags checksum provider.
    * @param string $bin
    *   The cache bin for which the object is created.
    */
-  public function __construct(Connection $connection, $bin) {
+  public function __construct(Connection $connection, CacheTagsChecksumInterface $checksum_provider, $bin) {
     // All cache tables should be prefixed with 'cache_'.
     $bin = 'cache_' . $bin;
 
     $this->bin = $bin;
     $this->connection = $connection;
+    $this->checksumProvider = $checksum_provider;
   }
 
   /**
@@ -76,7 +86,7 @@ public function getMultiple(&$cids, $allow_invalid = FALSE) {
     // ::select() is a much smaller proportion of the request.
     $result = array();
     try {
-      $result = $this->connection->query('SELECT cid, data, created, expire, serialized, tags, checksum_invalidations, checksum_deletions FROM {' . $this->connection->escapeTable($this->bin) . '} WHERE cid IN (:cids)', array(':cids' => array_keys($cid_mapping)));
+      $result = $this->connection->query('SELECT cid, data, created, expire, serialized, tags, checksum FROM {' . $this->connection->escapeTable($this->bin) . '} WHERE cid IN (:cids)', array(':cids' => array_keys($cid_mapping)));
     }
     catch (\Exception $e) {
       // Nothing to do.
@@ -116,18 +126,11 @@ protected function prepareItem($cache, $allow_invalid) {
 
     $cache->tags = $cache->tags ? explode(' ', $cache->tags) : array();
 
-    $checksum = $this->checksumTags($cache->tags);
-
-    // Check if deleteTags() has been called with any of the entry's tags.
-    if ($cache->checksum_deletions != $checksum['deletions']) {
-      return FALSE;
-    }
-
     // Check expire time.
     $cache->valid = $cache->expire == Cache::PERMANENT || $cache->expire >= REQUEST_TIME;
 
-    // Check if invalidateTags() has been called with any of the entry's tags.
-    if ($cache->checksum_invalidations != $checksum['invalidations']) {
+    // Check if invalidateTags() has been called with any of the items's tags.
+    if (!$this->checksumProvider->isValid($cache->checksum, $cache->tags)) {
       $cache->valid = FALSE;
     }
 
@@ -147,10 +150,12 @@ protected function prepareItem($cache, $allow_invalid) {
    * Implements Drupal\Core\Cache\CacheBackendInterface::set().
    */
   public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) {
-    Cache::validateTags($tags);
-    $tags = array_unique($tags);
-    // Sort the cache tags so that they are stored consistently in the database.
-    sort($tags);
+    if ($tags) {
+      Cache::validateTags($tags);
+      $tags = array_unique($tags);
+      // Sort the cache tags so that they are stored consistently in the database.
+      sort($tags);
+    }
     $try_again = FALSE;
     try {
       // The bin might not yet exist.
@@ -174,27 +179,11 @@ public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array
    * Actually set the cache.
    */
   protected function doSet($cid, $data, $expire, $tags) {
-    $deleted_tags = &drupal_static('Drupal\Core\Cache\DatabaseBackend::deletedTags', array());
-    $invalidated_tags = &drupal_static('Drupal\Core\Cache\DatabaseBackend::invalidatedTags', array());
-    // Remove tags that were already deleted or invalidated during this request
-    // from the static caches so that another deletion or invalidation can
-    // occur.
-    foreach ($tags as $tag) {
-      if (isset($deleted_tags[$tag])) {
-        unset($deleted_tags[$tag]);
-      }
-      if (isset($invalidated_tags[$tag])) {
-        unset($invalidated_tags[$tag]);
-      }
-    }
-    $checksum = $this->checksumTags($tags);
     $fields = array(
-      'serialized' => 0,
       'created' => round(microtime(TRUE), 3),
       'expire' => $expire,
       'tags' => implode(' ', $tags),
-      'checksum_invalidations' => $checksum['invalidations'],
-      'checksum_deletions' => $checksum['deletions'],
+      'checksum' => $this->checksumProvider->getCurrentChecksum($tags),
     );
     if (!is_string($data)) {
       $fields['data'] = serialize($data);
@@ -215,9 +204,6 @@ protected function doSet($cid, $data, $expire, $tags) {
    * {@inheritdoc}
    */
   public function setMultiple(array $items) {
-    $deleted_tags = &drupal_static('Drupal\Core\Cache\DatabaseBackend::deletedTags', array());
-    $invalidated_tags = &drupal_static('Drupal\Core\Cache\DatabaseBackend::invalidatedTags', array());
-
     // Use a transaction so that the database can write the changes in a single
     // commit.
     $transaction = $this->connection->startTransaction();
@@ -229,7 +215,7 @@ public function setMultiple(array $items) {
 
       $query = $this->connection
         ->insert($this->bin)
-        ->fields(array('cid', 'data', 'expire', 'created', 'serialized', 'tags', 'checksum_invalidations', 'checksum_deletions'));
+        ->fields(array('cid', 'data', 'expire', 'created', 'serialized', 'tags', 'checksum'));
 
       foreach ($items as $cid => $item) {
         $item += array(
@@ -237,32 +223,19 @@ public function setMultiple(array $items) {
           'tags' => array(),
         );
 
-        Cache::validateTags($item['tags']);
-        $item['tags'] = array_unique($item['tags']);
-        // Sort the cache tags so that they are stored consistently in the DB.
-        sort($item['tags']);
-
-        // Remove tags that were already deleted or invalidated during this
-        // request from the static caches so that another deletion or
-        // invalidation can occur.
-        foreach ($item['tags'] as $tag) {
-          if (isset($deleted_tags[$tag])) {
-            unset($deleted_tags[$tag]);
-          }
-          if (isset($invalidated_tags[$tag])) {
-            unset($invalidated_tags[$tag]);
-          }
+        if ($item['tags']) {
+          Cache::validateTags($item['tags']);
+          $item['tags'] = array_unique($item['tags']);
+          // Sort the cache tags so that they are stored consistently in the DB.
+          sort($item['tags']);
         }
 
-        $checksum = $this->checksumTags($item['tags']);
-
         $fields = array(
           'cid' => $cid,
           'expire' => $item['expire'],
           'created' => round(microtime(TRUE), 3),
           'tags' => implode(' ', $item['tags']),
-          'checksum_invalidations' => $checksum['invalidations'],
-          'checksum_deletions' => $checksum['deletions'],
+          'checksum' => $this->checksumProvider->getCurrentChecksum($item['tags']),
         );
 
         if (!is_string($item['data'])) {
@@ -317,32 +290,6 @@ public function deleteMultiple(array $cids) {
   }
 
   /**
-   * Implements Drupal\Core\Cache\CacheBackendInterface::deleteTags().
-   */
-  public function deleteTags(array $tags) {
-    $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array());
-    $deleted_tags = &drupal_static('Drupal\Core\Cache\DatabaseBackend::deletedTags', array());
-    foreach ($tags as $tag) {
-      // Only delete tags once per request unless they are written again.
-      if (isset($deleted_tags[$tag])) {
-        continue;
-      }
-      $deleted_tags[$tag] = TRUE;
-      unset($tag_cache[$tag]);
-      try {
-        $this->connection->merge('cachetags')
-          ->insertFields(array('deletions' => 1))
-          ->expression('deletions', 'deletions + 1')
-          ->key('tag', $tag)
-          ->execute();
-      }
-      catch (\Exception $e) {
-        $this->catchException($e, 'cachetags');
-      }
-    }
-  }
-
-  /**
    * Implements Drupal\Core\Cache\CacheBackendInterface::deleteAll().
    */
   public function deleteAll() {
@@ -386,32 +333,6 @@ public function invalidateMultiple(array $cids) {
   }
 
   /**
-   * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateTags().
-   */
-  public function invalidateTags(array $tags) {
-    try {
-      $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array());
-      $invalidated_tags = &drupal_static('Drupal\Core\Cache\DatabaseBackend::invalidatedTags', array());
-      foreach ($tags as $tag) {
-        // Only invalidate tags once per request unless they are written again.
-        if (isset($invalidated_tags[$tag])) {
-          continue;
-        }
-        $invalidated_tags[$tag] = TRUE;
-        unset($tag_cache[$tag]);
-        $this->connection->merge('cachetags')
-          ->insertFields(array('invalidations' => 1))
-          ->expression('invalidations', 'invalidations + 1')
-          ->key('tag', $tag)
-          ->execute();
-      }
-    }
-    catch (\Exception $e) {
-      $this->catchException($e, 'cachetags');
-    }
-  }
-
-  /**
    * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateAll().
    */
   public function invalidateAll() {
@@ -443,40 +364,6 @@ public function garbageCollection() {
   }
 
   /**
-   * Returns the sum total of validations for a given set of tags.
-   *
-   * @param array $tags
-   *   Array of cache tags.
-   *
-   * @return int
-   *   Sum of all invalidations.
-   */
-  protected function checksumTags(array $tags) {
-    $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array());
-
-    $checksum = array(
-      'invalidations' => 0,
-      'deletions' => 0,
-    );
-
-    $query_tags = array_diff($tags, array_keys($tag_cache));
-    if ($query_tags) {
-      $db_tags = $this->connection->query('SELECT tag, invalidations, deletions FROM {cachetags} WHERE tag IN (:tags)', array(':tags' => $query_tags))->fetchAllAssoc('tag', \PDO::FETCH_ASSOC);
-      $tag_cache += $db_tags;
-
-      // Fill static cache with empty objects for tags not found in the database.
-      $tag_cache += array_fill_keys(array_diff($query_tags, array_keys($db_tags)), $checksum);
-    }
-
-    foreach ($tags as $tag) {
-      $checksum['invalidations'] += $tag_cache[$tag]['invalidations'];
-      $checksum['deletions'] += $tag_cache[$tag]['deletions'];
-    }
-
-    return $checksum;
-  }
-
-  /**
    * {@inheritdoc}
    */
   public function removeBin() {
@@ -496,11 +383,7 @@ protected function ensureBinExists() {
       $database_schema = $this->connection->schema();
       if (!$database_schema->tableExists($this->bin)) {
         $schema_definition = $this->schemaDefinition();
-        $database_schema->createTable($this->bin, $schema_definition['bin']);
-        // If the bin doesn't exist, the cache tags table may also not exist.
-        if (!$database_schema->tableExists('cachetags')) {
-          $database_schema->createTable('cachetags', $schema_definition['cachetags']);
-        }
+        $database_schema->createTable($this->bin, $schema_definition);
         return TRUE;
       }
     }
@@ -516,14 +399,16 @@ protected function ensureBinExists() {
   /**
    * Act on an exception when cache might be stale.
    *
-   * If the {cachetags} table does not yet exist, that's fine but if the table
-   * exists and yet the query failed, then the cache is stale and the
-   * exception needs to propagate.
+   * If the table does not yet exist, that's fine, but if the table exists and
+   * yet the query failed, then the cache is stale and the exception needs to
+   * propagate.
    *
    * @param $e
    *   The exception.
    * @param string|null $table_name
-   *   The table name, defaults to $this->bin. Can be cachetags.
+   *   The table name. Defaults to $this->bin.
+   *
+   * @throws \Exception
    */
   protected function catchException(\Exception $e, $table_name = NULL) {
     if ($this->connection->schema()->tableExists($table_name ?: $this->bin)) {
@@ -552,10 +437,10 @@ protected function normalizeCid($cid) {
   }
 
   /**
-   * Defines the schema for the {cache_*} bin and {cachetags} tables.
+   * Defines the schema for the {cache_*} bin tables.
    */
   public function schemaDefinition() {
-    $schema['bin'] = array(
+    $schema = array(
       'description' => 'Storage for the cache API.',
       'fields' => array(
         'cid' => array(
@@ -599,49 +484,18 @@ public function schemaDefinition() {
           'size' => 'big',
           'not null' => FALSE,
         ),
-        'checksum_invalidations' => array(
+        'checksum' => array(
           'description' => 'The tag invalidation sum when this entry was saved.',
           'type' => 'int',
           'not null' => TRUE,
           'default' => 0,
         ),
-        'checksum_deletions' => array(
-          'description' => 'The tag deletion sum when this entry was saved.',
-          'type' => 'int',
-          'not null' => TRUE,
-          'default' => 0,
-        ),
       ),
       'indexes' => array(
         'expire' => array('expire'),
       ),
       'primary key' => array('cid'),
     );
-    $schema['cachetags'] = array(
-      'description' => 'Cache table for tracking cache tags related to the cache bin.',
-      'fields' => array(
-        'tag' => array(
-          'description' => 'Namespace-prefixed tag string.',
-          'type' => 'varchar',
-          'length' => 255,
-          'not null' => TRUE,
-          'default' => '',
-        ),
-        'invalidations' => array(
-          'description' => 'Number incremented when the tag is invalidated.',
-          'type' => 'int',
-          'not null' => TRUE,
-          'default' => 0,
-        ),
-        'deletions' => array(
-          'description' => 'Number incremented when the tag is deleted.',
-          'type' => 'int',
-          'not null' => TRUE,
-          'default' => 0,
-        ),
-      ),
-      'primary key' => array('tag'),
-    );
     return $schema;
   }
 }
diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackendFactory.php b/core/lib/Drupal/Core/Cache/DatabaseBackendFactory.php
index 1d70164..59b0b22 100644
--- a/core/lib/Drupal/Core/Cache/DatabaseBackendFactory.php
+++ b/core/lib/Drupal/Core/Cache/DatabaseBackendFactory.php
@@ -19,12 +19,23 @@ class DatabaseBackendFactory implements CacheFactoryInterface {
   protected $connection;
 
   /**
+   * The cache tags checksum provider.
+   *
+   * @var \Drupal\Core\Cache\CacheTagsChecksumInterface
+   */
+  protected $checksumProvider;
+
+  /**
    * Constructs the DatabaseBackendFactory object.
    *
    * @param \Drupal\Core\Database\Connection $connection
+   *   Database connection
+   * @param \Drupal\Core\Cache\CacheTagsChecksumInterface $checksum_provider
+   *   The cache tags checksum provider.
    */
-  function __construct(Connection $connection) {
+  function __construct(Connection $connection, CacheTagsChecksumInterface $checksum_provider) {
     $this->connection = $connection;
+    $this->checksumProvider = $checksum_provider;
   }
 
   /**
@@ -37,7 +48,7 @@ function __construct(Connection $connection) {
    *   The cache backend object for the specified cache bin.
    */
   function get($bin) {
-    return new DatabaseBackend($this->connection, $bin);
+    return new DatabaseBackend($this->connection, $this->checksumProvider, $bin);
   }
 
 }
diff --git a/core/lib/Drupal/Core/Cache/DatabaseCacheTagsChecksum.php b/core/lib/Drupal/Core/Cache/DatabaseCacheTagsChecksum.php
new file mode 100644
index 0000000..784294e
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/DatabaseCacheTagsChecksum.php
@@ -0,0 +1,205 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Cache\DatabaseCacheTagsChecksum.
+ */
+
+namespace Drupal\Core\Cache;
+
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Database\SchemaObjectExistsException;
+
+/**
+ * Cache tags invalidations checksum implementation that uses the database.
+ */
+class DatabaseCacheTagsChecksum implements CacheTagsChecksumInterface, CacheTagsInvalidatorInterface {
+
+  /**
+   * The database connection.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $connection;
+
+  /**
+   * Contains already loaded cache invalidations from the database.
+   *
+   * @var array
+   */
+  protected $tagCache = array();
+
+  /**
+   * A list of tags that have already been invalidated in this requested.
+   *
+   * Used to prevent the invalidation of the same cache tag multiple times.
+   *
+   * @var array
+   */
+  protected $invalidatedTags = array();
+
+  /**
+   * Constructs a DatabaseCacheTagsChecksum object.
+   *
+   * @param \Drupal\Core\Database\Connection $connection
+   *   The database connection.
+   */
+  public function __construct(Connection $connection) {
+    $this->connection = $connection;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function invalidateTags(array $tags) {
+    try {
+      foreach ($tags as $tag) {
+        // Only invalidate tags once per request unless they are written again.
+        if (isset($this->invalidatedTags[$tag])) {
+          continue;
+        }
+        $this->invalidatedTags[$tag] = TRUE;
+        unset($this->tagCache[$tag]);
+        $this->connection->merge('cachetags')
+          ->insertFields(array('invalidations' => 1))
+          ->expression('invalidations', 'invalidations + 1')
+          ->key('tag', $tag)
+          ->execute();
+      }
+    }
+    catch (\Exception $e) {
+      // Create the cache table, which will be empty. This fixes cases during
+      // core install where cache tags are invalidated before the table is
+      // created.
+      if (!$this->ensureTableExists()) {
+        $this->catchException($e);
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCurrentChecksum(array $tags) {
+    // Remove tags that were already invalidated during this request from the
+    // static caches so that another deletion or invalidation can occur.
+    foreach ($tags as $tag) {
+      unset($this->invalidatedTags[$tag]);
+    }
+    return $this->calculateChecksum($tags);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isValid($checksum, array $tags) {
+    return $checksum == $this->calculateChecksum($tags);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function calculateChecksum(array $tags) {
+    $checksum = 0;
+
+    $query_tags = array_diff($tags, array_keys($this->tagCache));
+    if ($query_tags) {
+      $db_tags = array();
+      try {
+        $db_tags = $this->connection->query('SELECT tag, invalidations FROM {cachetags} WHERE tag IN (:tags)', array(':tags' => $query_tags))
+          ->fetchAllKeyed();
+        $this->tagCache += $db_tags;
+      }
+      catch (\Exception $e) {
+        // If the table does not exist yet, create.
+        if (!$this->ensureTableExists()) {
+          $this->catchException($e);
+        }
+      }
+      // Fill static cache with empty objects for tags not found in the database.
+      $this->tagCache += array_fill_keys(array_diff($query_tags, array_keys($db_tags)), 0);
+    }
+
+    foreach ($tags as $tag) {
+      $checksum += $this->tagCache[$tag];
+    }
+
+    return $checksum;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function reset() {
+    $this->tagCache = array();
+    $this->invalidatedTags = array();
+  }
+
+  /**
+   * Check if the cache tags table exists and create it if not.
+   */
+  protected function ensureTableExists() {
+    try {
+      $database_schema = $this->connection->schema();
+      // Create the cache tags table if it does not exist.
+      if (!$database_schema->tableExists('cachetags')) {
+        $schema_definition = $this->schemaDefinition();
+        $database_schema->createTable('cachetags', $schema_definition);
+
+        return TRUE;
+      }
+    }
+    // If another process has already created the cachetags table, attempting to
+    // recreate it will throw an exception. In this case just catch the
+    // exception and do nothing.
+    catch (SchemaObjectExistsException $e) {
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
+   * Defines the schema for the {cachetags} table.
+   */
+  public function schemaDefinition() {
+    $schema = array(
+      'description' => 'Cache table for tracking cache tag invalidations.',
+      'fields' => array(
+        'tag' => array(
+          'description' => 'Namespace-prefixed tag string.',
+          'type' => 'varchar',
+          'length' => 255,
+          'not null' => TRUE,
+          'default' => '',
+        ),
+        'invalidations' => array(
+          'description' => 'Number incremented when the tag is invalidated.',
+          'type' => 'int',
+          'not null' => TRUE,
+          'default' => 0,
+        ),
+      ),
+      'primary key' => array('tag'),
+    );
+    return $schema;
+  }
+
+  /**
+   * Act on an exception when cache might be stale.
+   *
+   * If the {cachetags} table does not yet exist, that's fine but if the table
+   * exists and yet the query failed, then the cache is stale and the
+   * exception needs to propagate.
+   *
+   * @param \Exception $e
+   *   The exception.
+   *
+   * @throws \Exception
+   */
+  protected function catchException(\Exception $e) {
+    if ($this->connection->schema()->tableExists('cachetags')) {
+      throw $e;
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Cache/MemoryBackend.php b/core/lib/Drupal/Core/Cache/MemoryBackend.php
index 23fddc5..5d7cfab 100644
--- a/core/lib/Drupal/Core/Cache/MemoryBackend.php
+++ b/core/lib/Drupal/Core/Cache/MemoryBackend.php
@@ -17,7 +17,7 @@
  *
  * @ingroup cache
  */
-class MemoryBackend implements CacheBackendInterface {
+class MemoryBackend implements CacheBackendInterface, CacheTagsInvalidatorInterface {
 
   /**
    * Array to store cache objects.
@@ -144,17 +144,6 @@ public function deleteMultiple(array $cids) {
   }
 
   /**
-   * Implements Drupal\Core\Cache\CacheBackendInterface::deleteTags().
-   */
-  public function deleteTags(array $tags) {
-    foreach ($this->cache as $cid => $item) {
-      if (array_intersect($tags, $item->tags)) {
-        unset($this->cache[$cid]);
-      }
-    }
-  }
-
-  /**
    * Implements Drupal\Core\Cache\CacheBackendInterface::deleteAll().
    */
   public function deleteAll() {
@@ -180,7 +169,7 @@ public function invalidateMultiple(array $cids) {
   }
 
   /**
-   * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateTags().
+   * {@inheritdoc}
    */
   public function invalidateTags(array $tags) {
     foreach ($this->cache as $cid => $item) {
diff --git a/core/lib/Drupal/Core/Cache/MemoryBackendFactory.php b/core/lib/Drupal/Core/Cache/MemoryBackendFactory.php
index c9e288d..53e5b07 100644
--- a/core/lib/Drupal/Core/Cache/MemoryBackendFactory.php
+++ b/core/lib/Drupal/Core/Cache/MemoryBackendFactory.php
@@ -10,10 +10,20 @@
 class MemoryBackendFactory implements CacheFactoryInterface {
 
   /**
+   * Instantiated memory cache bins.
+   *
+   * @var \Drupal\Core\Cache\MemoryBackend[]
+   */
+  protected $bins = array();
+
+  /**
    * {@inheritdoc}
    */
   function get($bin) {
-    return new MemoryBackend($bin);
+    if (!isset($this->bins[$bin])) {
+      $this->bins[$bin] = new MemoryBackend($bin);
+    }
+    return $this->bins[$bin];
   }
 
 }
diff --git a/core/lib/Drupal/Core/Cache/NullBackend.php b/core/lib/Drupal/Core/Cache/NullBackend.php
index d27bc13..852da7b 100644
--- a/core/lib/Drupal/Core/Cache/NullBackend.php
+++ b/core/lib/Drupal/Core/Cache/NullBackend.php
@@ -70,11 +70,6 @@ public function deleteMultiple(array $cids) {}
   public function deleteAll() {}
 
   /**
-   * Implements Drupal\Core\Cache\CacheBackendInterface::deleteTags().
-   */
-  public function deleteTags(array $tags) {}
-
-  /**
    * Implements Drupal\Core\Cache\CacheBackendInterface::invalidate().
    */
   public function invalidate($cid) {}
@@ -85,11 +80,6 @@ public function invalidate($cid) {}
   public function invalidateMultiple(array $cids) {}
 
   /**
-   * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateTags().
-   */
-  public function invalidateTags(array $tags) {}
-
-  /**
    * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateAll().
    */
   public function invalidateAll() {}
diff --git a/core/lib/Drupal/Core/Cache/PhpBackend.php b/core/lib/Drupal/Core/Cache/PhpBackend.php
index e188ffb..df6ffa7 100644
--- a/core/lib/Drupal/Core/Cache/PhpBackend.php
+++ b/core/lib/Drupal/Core/Cache/PhpBackend.php
@@ -36,13 +36,23 @@ class PhpBackend implements CacheBackendInterface {
   protected $cache = array();
 
   /**
+   * The cache tags checksum provider.
+   *
+   * @var \Drupal\Core\Cache\CacheTagsChecksumInterface
+   */
+  protected $checksumProvider;
+
+  /**
    * Constructs a PhpBackend object.
    *
    * @param string $bin
    *   The cache bin for which the object is created.
+   * @param \Drupal\Core\Cache\CacheTagsChecksumInterface $checksum_provider
+   *   The cache tags checksum provider.
    */
-  public function __construct($bin) {
+  public function __construct($bin, CacheTagsChecksumInterface $checksum_provider) {
     $this->bin = 'cache_' . $bin;
+    $this->checksumProvider = $checksum_provider;
   }
 
   /**
@@ -122,6 +132,11 @@ protected function prepareItem($cache, $allow_invalid) {
     // Check expire time.
     $cache->valid = $cache->expire == Cache::PERMANENT || $cache->expire >= REQUEST_TIME;
 
+    // Check if invalidateTags() has been called with any of the item's tags.
+    if (!$this->checksumProvider->isValid($cache->checksum, $cache->tags)) {
+      $cache->valid = FALSE;
+    }
+
     if (!$allow_invalid && !$cache->valid) {
       return FALSE;
     }
@@ -133,13 +148,17 @@ protected function prepareItem($cache, $allow_invalid) {
    * {@inheritdoc}
    */
   public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) {
-    Cache::validateTags($tags);
+    if ($tags) {
+      $tags = array_unique($tags);
+      Cache::validateTags($tags);
+    }
     $item = (object) array(
       'cid' => $cid,
       'data' => $data,
       'created' => round(microtime(TRUE), 3),
       'expire' => $expire,
-      'tags' => array_unique($tags),
+      'tags' => $tags,
+      'checksum' => $this->checksumProvider->getCurrentChecksum($tags),
     );
     $this->writeItem($this->normalizeCid($cid), $item);
   }
@@ -163,18 +182,6 @@ public function deleteMultiple(array $cids) {
   /**
    * {@inheritdoc}
    */
-  public function deleteTags(array $tags) {
-    foreach ($this->storage()->listAll() as $cidhash) {
-      $item = $this->getByHash($cidhash);
-      if (is_object($item) && array_intersect($tags, $item->tags)) {
-        $this->delete($item->cid);
-      }
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function deleteAll() {
     $this->storage()->deleteAll();
   }
diff --git a/core/lib/Drupal/Core/Cache/PhpBackendFactory.php b/core/lib/Drupal/Core/Cache/PhpBackendFactory.php
index 0801b72..dde93d4 100644
--- a/core/lib/Drupal/Core/Cache/PhpBackendFactory.php
+++ b/core/lib/Drupal/Core/Cache/PhpBackendFactory.php
@@ -10,6 +10,23 @@
 class PhpBackendFactory implements CacheFactoryInterface {
 
   /**
+   * The cache tags checksum provider.
+   *
+   * @var \Drupal\Core\Cache\CacheTagsChecksumInterface
+   */
+  protected $checksumProvider;
+
+  /**
+   * Constructs a PhpBackendFactory object.
+   *
+   * @param \Drupal\Core\Cache\CacheTagsChecksumInterface $checksum_provider
+   *   The cache tags checksum provider.
+   */
+  public function __construct(CacheTagsChecksumInterface $checksum_provider) {
+    $this->checksumProvider = $checksum_provider;
+  }
+
+  /**
    * Gets PhpBackend for the specified cache bin.
    *
    * @param $bin
@@ -19,7 +36,7 @@ class PhpBackendFactory implements CacheFactoryInterface {
    *   The cache backend object for the specified cache bin.
    */
   function get($bin) {
-    return new PhpBackend($bin);
+    return new PhpBackend($bin, $this->checksumProvider);
   }
 
 }
diff --git a/core/lib/Drupal/Core/Config/CachedStorage.php b/core/lib/Drupal/Core/Config/CachedStorage.php
index b657f66..5023c35 100644
--- a/core/lib/Drupal/Core/Config/CachedStorage.php
+++ b/core/lib/Drupal/Core/Config/CachedStorage.php
@@ -131,7 +131,7 @@ public function write($name, array $data) {
       // While not all written data is read back, setting the cache instead of
       // just deleting it avoids cache rebuild stampedes.
       $this->cache->set($this->getCacheKey($name), $data);
-      Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG));
+      Cache::invalidateTags(array($this::FIND_BY_PREFIX_CACHE_TAG));
       $this->findByPrefixCache = array();
       return TRUE;
     }
@@ -146,7 +146,7 @@ public function delete($name) {
     // rebuilding the cache before the storage is gone.
     if ($this->storage->delete($name)) {
       $this->cache->delete($this->getCacheKey($name));
-      Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG));
+      Cache::invalidateTags(array($this::FIND_BY_PREFIX_CACHE_TAG));
       $this->findByPrefixCache = array();
       return TRUE;
     }
@@ -162,7 +162,7 @@ public function rename($name, $new_name) {
     if ($this->storage->rename($name, $new_name)) {
       $this->cache->delete($this->getCacheKey($name));
       $this->cache->delete($this->getCacheKey($new_name));
-      Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG));
+      Cache::invalidateTags(array($this::FIND_BY_PREFIX_CACHE_TAG));
       $this->findByPrefixCache = array();
       return TRUE;
     }
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index 7d7eded..cd9c9a7 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -671,7 +671,7 @@ public function clearCachedFieldDefinitions() {
     $this->fieldMapByFieldType = array();
     $this->displayModeInfo = array();
     $this->extraFields = array();
-    Cache::deleteTags(array('entity_field_info'));
+    Cache::invalidateTags(array('entity_field_info'));
     // The typed data manager statically caches prototype objects with injected
     // definitions, clear those as well.
     $this->typedDataManager->clearCachedDefinitions();
@@ -682,7 +682,7 @@ public function clearCachedFieldDefinitions() {
    */
   public function clearCachedBundles() {
     $this->bundleInfo = array();
-    Cache::deleteTags(array('entity_bundles'));
+    Cache::invalidateTags(array('entity_bundles'));
     // Entity bundles are exposed as data types, clear that cache too.
     $this->typedDataManager->clearCachedDefinitions();
   }
diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
index 40a695e..7afae45 100644
--- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
+++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Entity\Sql;
 
 use Drupal\Component\Utility\String;
+use Drupal\Core\Cache\Cache;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Database\Database;
@@ -574,7 +575,7 @@ public function resetCache(array $ids = NULL) {
     else {
       $this->entities = array();
       if ($this->entityType->isPersistentlyCacheable()) {
-        $this->cacheBackend->deleteTags(array($this->entityTypeId . '_values'));
+        Cache::invalidateTags(array($this->entityTypeId . '_values'));
       }
     }
   }
diff --git a/core/lib/Drupal/Core/EventSubscriber/MenuRouterRebuildSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/MenuRouterRebuildSubscriber.php
index 41b5a87..57cc618 100644
--- a/core/lib/Drupal/Core/EventSubscriber/MenuRouterRebuildSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/MenuRouterRebuildSubscriber.php
@@ -57,7 +57,7 @@ public function __construct(LockBackendInterface $lock, MenuLinkManagerInterface
    */
   public function onRouterRebuild(Event $event) {
     $this->menuLinksRebuild();
-    Cache::deleteTags(array('local_task'));
+    Cache::invalidateTags(array('local_task'));
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Extension/ThemeHandler.php b/core/lib/Drupal/Core/Extension/ThemeHandler.php
index 8beb184..89ad503 100644
--- a/core/lib/Drupal/Core/Extension/ThemeHandler.php
+++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
@@ -621,7 +621,7 @@ protected function resetSystem() {
 
     // @todo It feels wrong to have the requirement to clear the local tasks
     //   cache here.
-    Cache::deleteTags(array('local_task'));
+    Cache::invalidateTags(array('local_task'));
     $this->themeRegistryRebuild();
   }
 
diff --git a/core/lib/Drupal/Core/Field/FieldStorageDefinitionInterface.php b/core/lib/Drupal/Core/Field/FieldStorageDefinitionInterface.php
index 9b0eb65..d18a8c7 100644
--- a/core/lib/Drupal/Core/Field/FieldStorageDefinitionInterface.php
+++ b/core/lib/Drupal/Core/Field/FieldStorageDefinitionInterface.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Field;
 
 use Drupal\Core\Entity\FieldableEntityInterface;
+use Drupal\Core\TypedData\DataDefinitionInterface;
 
 /**
  * Defines an interface for entity field storage definitions.
diff --git a/core/lib/Drupal/Core/Installer/InstallerServiceProvider.php b/core/lib/Drupal/Core/Installer/InstallerServiceProvider.php
index 6e93561..15a7e79 100644
--- a/core/lib/Drupal/Core/Installer/InstallerServiceProvider.php
+++ b/core/lib/Drupal/Core/Installer/InstallerServiceProvider.php
@@ -49,6 +49,12 @@ public function register(ContainerBuilder $container) {
     $container
       ->register('router.dumper', 'Drupal\Core\Routing\NullMatcherDumper');
 
+    // Remove the cache tags invalidator tag from the cache tags storage, so
+    // that we don't call it when cache tags are invalidated very early in the
+    // installer.
+    $container->getDefinition('cache_tags.checksum')
+      ->clearTag('cache_tags.invalidator');
+
     // Replace the route builder with an empty implementation.
     // @todo Convert installer steps into routes; add an installer.routing.yml.
     $definition = $container->getDefinition('router.builder');
diff --git a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
index 5828d33..95928e4 100644
--- a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
+++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
@@ -161,7 +161,7 @@ public function clearCachedDefinitions() {
     if ($this->cacheBackend) {
       if ($this->cacheTags) {
         // Use the cache tags to clear the cache.
-        Cache::deleteTags($this->cacheTags);
+        Cache::invalidateTags($this->cacheTags);
       }
       else {
         $this->cacheBackend->delete($this->cacheKey);
diff --git a/core/lib/Drupal/Core/Utility/Token.php b/core/lib/Drupal/Core/Utility/Token.php
index 4cfc63f..dc7d162 100644
--- a/core/lib/Drupal/Core/Utility/Token.php
+++ b/core/lib/Drupal/Core/Utility/Token.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Utility;
 
+use Drupal\Core\Cache\Cache;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Language\LanguageInterface;
@@ -346,7 +347,7 @@ public function setInfo(array $tokens) {
    */
   public function resetInfo() {
     $this->tokenInfo = NULL;
-    $this->cache->deleteTags(array(
+    Cache::invalidateTags(array(
       static::TOKEN_INFO_CACHE_TAG => TRUE,
     ));
   }
diff --git a/core/modules/book/src/BookManager.php b/core/modules/book/src/BookManager.php
index 3f5cfdc..3659f8d 100644
--- a/core/modules/book/src/BookManager.php
+++ b/core/modules/book/src/BookManager.php
@@ -433,7 +433,7 @@ public function deleteFromBook($nid) {
     }
     $this->updateOriginalParent($original);
     $this->books = NULL;
-    \Drupal::cache('data')->deleteTags(array('bid:' . $original['bid']));
+    Cache::invalidateTags(array('bid:' . $original['bid']));
   }
 
   /**
@@ -763,7 +763,7 @@ public function saveBookLink(array $link, $new) {
     foreach ($affected_bids as $bid) {
       $cache_tags[] = 'bid:' . $bid;
     }
-    \Drupal::cache('data')->deleteTags($cache_tags);
+    Cache::invalidateTags($cache_tags);
     return $link;
   }
 
diff --git a/core/modules/config/src/Tests/Storage/CachedStorageTest.php b/core/modules/config/src/Tests/Storage/CachedStorageTest.php
index 11c3599..f7dd32e 100644
--- a/core/modules/config/src/Tests/Storage/CachedStorageTest.php
+++ b/core/modules/config/src/Tests/Storage/CachedStorageTest.php
@@ -90,7 +90,8 @@ public function containerBuild(ContainerBuilder $container) {
     parent::containerBuild($container);
     // Use the regular database cache backend to aid testing.
     $container->register('cache_factory', 'Drupal\Core\Cache\DatabaseBackendFactory')
-      ->addArgument(new Reference('database'));
+      ->addArgument(new Reference('database'))
+      ->addArgument(new Reference('cache_tags.checksum'));
   }
 
 }
diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module
index 82cff29..9e09a46 100644
--- a/core/modules/locale/locale.module
+++ b/core/modules/locale/locale.module
@@ -986,7 +986,7 @@ function _locale_refresh_translations($langcodes, $lids = array()) {
     }
   }
   // Clear locale cache.
-  Cache::deleteTags(array('locale'));
+  Cache::invalidateTags(array('locale'));
 }
 
 /**
diff --git a/core/modules/locale/src/Tests/LocaleTranslationUiTest.php b/core/modules/locale/src/Tests/LocaleTranslationUiTest.php
index e9919cd..f918e1e 100644
--- a/core/modules/locale/src/Tests/LocaleTranslationUiTest.php
+++ b/core/modules/locale/src/Tests/LocaleTranslationUiTest.php
@@ -136,8 +136,8 @@ public function testStringTranslation() {
     $this->assertRaw($translation_to_en, 'English translation properly saved.');
 
     // Reset the tag cache on the tester side in order to pick up the call to
-    // Cache::deleteTags() on the tested side.
-    drupal_static_reset('Drupal\Core\Cache\CacheBackendInterface::tagCache');
+    // Cache::invalidateTags() on the tested side.
+    \Drupal::service('cache_tags.checksum')->reset();
 
     $this->assertTrue($name != $translation && t($name, array(), array('langcode' => $langcode)) == $translation, 't() works for non-English.');
     // Refresh the locale() cache to get fresh data from t() below. We are in
diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php
index 1f62ef4..51e5940 100644
--- a/core/modules/simpletest/src/WebTestBase.php
+++ b/core/modules/simpletest/src/WebTestBase.php
@@ -1151,12 +1151,7 @@ protected function resetAll() {
    */
   protected function refreshVariables() {
     // Clear the tag cache.
-    // @todo Replace drupal_static() usage within classes and provide a
-    //   proper interface for invoking reset() on a cache backend:
-    //   https://www.drupal.org/node/2311945.
-    drupal_static_reset('Drupal\Core\Cache\CacheBackendInterface::tagCache');
-    drupal_static_reset('Drupal\Core\Cache\DatabaseBackend::deletedTags');
-    drupal_static_reset('Drupal\Core\Cache\DatabaseBackend::invalidatedTags');
+    \Drupal::service('cache_tags.checksum')->reset();
     foreach (Cache::getBins() as $backend) {
       if (is_callable(array($backend, 'reset'))) {
         $backend->reset();
diff --git a/core/modules/system/core.api.php b/core/modules/system/core.api.php
index ad73fa0..5ce09ab 100644
--- a/core/modules/system/core.api.php
+++ b/core/modules/system/core.api.php
@@ -495,8 +495,7 @@
  * );
  * \Drupal::cache()->set($cid, $data, CacheBackendInterface::CACHE_PERMANENT, $tags);
  *
- * // Delete or invalidate all cache items with certain tags.
- * \Drupal\Core\Cache\Cache::deleteTags(array('node:1'));
+ * // Invalidate all cache items with certain tags.
  * \Drupal\Core\Cache\Cache::invalidateTags(array('user:1'));
  * @endcode
  *
diff --git a/core/modules/system/src/Tests/Cache/ApcuBackendUnitTest.php b/core/modules/system/src/Tests/Cache/ApcuBackendUnitTest.php
index da8f565..14bb16c 100644
--- a/core/modules/system/src/Tests/Cache/ApcuBackendUnitTest.php
+++ b/core/modules/system/src/Tests/Cache/ApcuBackendUnitTest.php
@@ -34,7 +34,8 @@ protected function checkRequirements() {
   }
 
   protected function createCacheBackend($bin) {
-    return new ApcuBackend($bin, $this->databasePrefix);
+    $backend = new ApcuBackend($bin, $this->databasePrefix, \Drupal::service('cache_tags.checksum'));
+    return $backend;
   }
 
   protected function tearDown() {
diff --git a/core/modules/system/src/Tests/Cache/BackendChainUnitTest.php b/core/modules/system/src/Tests/Cache/BackendChainUnitTest.php
index c19a8ae..4c90701 100644
--- a/core/modules/system/src/Tests/Cache/BackendChainUnitTest.php
+++ b/core/modules/system/src/Tests/Cache/BackendChainUnitTest.php
@@ -26,6 +26,8 @@ protected function createCacheBackend($bin) {
       ->prependBackend(new MemoryBackend('bar'))
       ->appendBackend(new MemoryBackend('baz'));
 
+    \Drupal::service('cache_tags.invalidator')->addInvalidator($chain);
+
     return $chain;
   }
 
diff --git a/core/modules/system/src/Tests/Cache/ChainedFastBackendUnitTest.php b/core/modules/system/src/Tests/Cache/ChainedFastBackendUnitTest.php
index 32488a6..961c10a 100644
--- a/core/modules/system/src/Tests/Cache/ChainedFastBackendUnitTest.php
+++ b/core/modules/system/src/Tests/Cache/ChainedFastBackendUnitTest.php
@@ -25,9 +25,13 @@ class ChainedFastBackendUnitTest extends GenericCacheBackendUnitTestBase {
    *   A new ChainedFastBackend object.
    */
   protected function createCacheBackend($bin) {
-    $consistent_backend = new DatabaseBackend($this->container->get('database'), $bin);
-    $fast_backend = new PhpBackend($bin);
-    return new ChainedFastBackend($consistent_backend, $fast_backend, $bin);
+    $consistent_backend = new DatabaseBackend(\Drupal::service('database'), \Drupal::service('cache_tags.checksum'), $bin);
+    $fast_backend = new PhpBackend($bin, \Drupal::service('cache_tags.checksum'));
+    $backend = new ChainedFastBackend($consistent_backend, $fast_backend, $bin);
+    // Explicitly register the cache bin as it can not work through the
+    // cache bin list in the container.
+    \Drupal::service('cache_tags.invalidator')->addInvalidator($backend);
+    return $backend;
   }
 
 }
diff --git a/core/modules/system/src/Tests/Cache/DatabaseBackendTagTest.php b/core/modules/system/src/Tests/Cache/DatabaseBackendTagTest.php
index e2b9d37..24cef4d 100644
--- a/core/modules/system/src/Tests/Cache/DatabaseBackendTagTest.php
+++ b/core/modules/system/src/Tests/Cache/DatabaseBackendTagTest.php
@@ -62,28 +62,4 @@ public function testTagInvalidations() {
     $this->assertEqual($invalidations_after, $invalidations_before + 1, 'Only one addition cache tag invalidation has occurred after invalidating a tag used in multiple bins.');
   }
 
-  public function testTagDeletions() {
-    // Create cache entry in multiple bins.
-    $tags = array('test_tag:1', 'test_tag:2', 'test_tag:3');
-    $bins = array('data', 'bootstrap', 'render');
-    foreach ($bins as $bin) {
-      $bin = \Drupal::cache($bin);
-      $bin->set('test', 'value', Cache::PERMANENT, $tags);
-      $this->assertTrue($bin->get('test'), 'Cache item was set in bin.');
-    }
-
-    $deletions_before = intval(db_select('cachetags')->fields('cachetags', array('deletions'))->condition('tag', 'test_tag:2')->execute()->fetchField());
-    Cache::deleteTags(array('test_tag:2'));
-
-    // Test that cache entry has been deleted in multiple bins.
-    foreach ($bins as $bin) {
-      $bin = \Drupal::cache($bin);
-      $this->assertFalse($bin->get('test'), 'Tag invalidation affected item in bin.');
-    }
-
-    // Test that only one tag deletion has occurred.
-    $deletions_after = intval(db_select('cachetags')->fields('cachetags', array('deletions'))->condition('tag', 'test_tag:2')->execute()->fetchField());
-    $this->assertEqual($deletions_after, $deletions_before + 1, 'Only one addition cache tag deletion has occurred after deleting a tag used in multiple bins.');
-  }
-
 }
diff --git a/core/modules/system/src/Tests/Cache/DatabaseBackendUnitTest.php b/core/modules/system/src/Tests/Cache/DatabaseBackendUnitTest.php
index bd2952e..91009f8 100644
--- a/core/modules/system/src/Tests/Cache/DatabaseBackendUnitTest.php
+++ b/core/modules/system/src/Tests/Cache/DatabaseBackendUnitTest.php
@@ -30,7 +30,7 @@ class DatabaseBackendUnitTest extends GenericCacheBackendUnitTestBase {
    *   A new DatabaseBackend object.
    */
   protected function createCacheBackend($bin) {
-    return new DatabaseBackend($this->container->get('database'), $bin);
+    return new DatabaseBackend($this->container->get('database'), $this->container->get('cache_tags.checksum'), $bin);
   }
 
 }
diff --git a/core/modules/system/src/Tests/Cache/GenericCacheBackendUnitTestBase.php b/core/modules/system/src/Tests/Cache/GenericCacheBackendUnitTestBase.php
index d6f31bf..463be08 100644
--- a/core/modules/system/src/Tests/Cache/GenericCacheBackendUnitTestBase.php
+++ b/core/modules/system/src/Tests/Cache/GenericCacheBackendUnitTestBase.php
@@ -473,66 +473,6 @@ public function testDeleteMultiple() {
   }
 
   /**
-   * Tests Drupal\Core\Cache\CacheBackendInterface::deleteTags().
-   */
-  function testDeleteTags() {
-    $backend = $this->getCacheBackend();
-
-    // Create two cache entries with the same tag and tag value.
-    $backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag:2'));
-    $backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag:2'));
-    $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Two cache items were created.');
-
-    // Delete test_tag of value 1. This should delete both entries.
-    $backend->deleteTags(array('test_tag:2'));
-    $this->assertFalse($backend->get('test_cid_invalidate1') || $backend->get('test_cid_invalidate2'), 'Two cache items invalidated after deleting a cache tag.');
-    $this->assertFalse($backend->get('test_cid_invalidate1', TRUE) || $backend->get('test_cid_invalidate2', TRUE), 'Two cache items deleted after deleting a cache tag.');
-
-    // Create two cache entries with the same tag and an array tag value.
-    $backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag:1'));
-    $backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag:1'));
-    $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Two cache items were created.');
-
-    // Delete test_tag of value 1. This should delete both entries.
-    $backend->deleteTags(array('test_tag:1'));
-    $this->assertFalse($backend->get('test_cid_invalidate1') || $backend->get('test_cid_invalidate2'), 'Two cache items invalidated after deleted a cache tag.');
-    $this->assertFalse($backend->get('test_cid_invalidate1', TRUE) || $backend->get('test_cid_invalidate2', TRUE), 'Two cache items deleted after deleting a cache tag.');
-
-    // Create three cache entries with a mix of tags and tag values.
-    $backend->set('test_cid_invalidate1', $this->defaultValue, Cache::PERMANENT, array('test_tag:1'));
-    $backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag:2'));
-    $backend->set('test_cid_invalidate3', $this->defaultValue, Cache::PERMANENT, array('test_tag_foo:3'));
-    $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2') && $backend->get('test_cid_invalidate3'), 'Three cached items were created.');
-    $backend->deleteTags(array('test_tag_foo:3'));
-    $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Cached items not matching the tag were not deleted.');
-    $this->assertFalse($backend->get('test_cid_invalidated3', TRUE), 'Cache item matching the tag was deleted.');
-
-    // Create cache entry in multiple bins. Two cache entries
-    // (test_cid_invalidate1 and test_cid_invalidate2) still exist from previous
-    // tests.
-    $tags = array('test_tag:1', 'test_tag:2', 'test_tag:3');
-    $bins = array('path', 'bootstrap', 'page');
-    foreach ($bins as $bin) {
-      $this->getCacheBackend($bin)->set('test', $this->defaultValue, Cache::PERMANENT, $tags);
-      $this->assertTrue($this->getCacheBackend($bin)->get('test'), 'Cache item was set in bin.');
-    }
-
-    // Delete tag in mulitple bins.
-    foreach ($bins as $bin) {
-      $this->getCacheBackend($bin)->deleteTags(array('test_tag:2'));
-    }
-
-    // Test that cache entry has been deleted in multple bins.
-    foreach ($bins as $bin) {
-      $this->assertFalse($this->getCacheBackend($bin)->get('test', TRUE), 'Tag deletion affected item in bin.');
-    }
-    // Test that the cache entry with a matching tag has been invalidated.
-    $this->assertFalse($this->getCacheBackend($bin)->get('test_cid_invalidate2', TRUE), 'Cache items matching tag were deleted.');
-    // Test that the cache entry with without a matching tag still exists.
-    $this->assertTrue($this->getCacheBackend($bin)->get('test_cid_invalidate1'), 'Cache items not matching tag were not invalidated.');
-  }
-
-  /**
    * Test Drupal\Core\Cache\CacheBackendInterface::deleteAll().
    */
   public function testDeleteAll() {
@@ -596,7 +536,7 @@ function testInvalidateTags() {
     $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Two cache items were created.');
 
     // Invalidate test_tag of value 1. This should invalidate both entries.
-    $backend->invalidateTags(array('test_tag:2'));
+    Cache::invalidateTags(array('test_tag:2'));
     $this->assertFalse($backend->get('test_cid_invalidate1') || $backend->get('test_cid_invalidate2'), 'Two cache items invalidated after invalidating a cache tag.');
     $this->assertTrue($backend->get('test_cid_invalidate1', TRUE) && $backend->get('test_cid_invalidate2', TRUE), 'Cache items not deleted after invalidating a cache tag.');
 
@@ -606,7 +546,7 @@ function testInvalidateTags() {
     $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Two cache items were created.');
 
     // Invalidate test_tag of value 1. This should invalidate both entries.
-    $backend->invalidateTags(array('test_tag:1'));
+    Cache::invalidateTags(array('test_tag:1'));
     $this->assertFalse($backend->get('test_cid_invalidate1') || $backend->get('test_cid_invalidate2'), 'Two caches removed after invalidating a cache tag.');
     $this->assertTrue($backend->get('test_cid_invalidate1', TRUE) && $backend->get('test_cid_invalidate2', TRUE), 'Cache items not deleted after invalidating a cache tag.');
 
@@ -615,7 +555,7 @@ function testInvalidateTags() {
     $backend->set('test_cid_invalidate2', $this->defaultValue, Cache::PERMANENT, array('test_tag:2'));
     $backend->set('test_cid_invalidate3', $this->defaultValue, Cache::PERMANENT, array('test_tag_foo:3'));
     $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2') && $backend->get('test_cid_invalidate3'), 'Three cached items were created.');
-    $backend->invalidateTags(array('test_tag_foo:3'));
+    Cache::invalidateTags(array('test_tag_foo:3'));
     $this->assertTrue($backend->get('test_cid_invalidate1') && $backend->get('test_cid_invalidate2'), 'Cache items not matching the tag were not invalidated.');
     $this->assertFalse($backend->get('test_cid_invalidated3'), 'Cached item matching the tag was removed.');
 
@@ -629,10 +569,7 @@ function testInvalidateTags() {
       $this->assertTrue($this->getCacheBackend($bin)->get('test'), 'Cache item was set in bin.');
     }
 
-    // Invalidate tag in mulitple bins.
-    foreach ($bins as $bin) {
-      $this->getCacheBackend($bin)->invalidateTags(array('test_tag:2'));
-    }
+    Cache::invalidateTags(array('test_tag:2'));
 
     // Test that cache entry has been invalidated in multple bins.
     foreach ($bins as $bin) {
diff --git a/core/modules/system/src/Tests/Cache/MemoryBackendUnitTest.php b/core/modules/system/src/Tests/Cache/MemoryBackendUnitTest.php
index 85edfde..0dfeae7 100644
--- a/core/modules/system/src/Tests/Cache/MemoryBackendUnitTest.php
+++ b/core/modules/system/src/Tests/Cache/MemoryBackendUnitTest.php
@@ -23,6 +23,8 @@ class MemoryBackendUnitTest extends GenericCacheBackendUnitTestBase {
    *   A new MemoryBackend object.
    */
   protected function createCacheBackend($bin) {
-    return new MemoryBackend($bin);
+    $backend = new MemoryBackend($bin);
+    \Drupal::service('cache_tags.invalidator')->addInvalidator($backend);
+    return $backend;
   }
 }
diff --git a/core/modules/system/src/Tests/Cache/PhpBackendUnitTest.php b/core/modules/system/src/Tests/Cache/PhpBackendUnitTest.php
index 57df19b..507c16f 100644
--- a/core/modules/system/src/Tests/Cache/PhpBackendUnitTest.php
+++ b/core/modules/system/src/Tests/Cache/PhpBackendUnitTest.php
@@ -23,7 +23,8 @@ class PhpBackendUnitTest extends GenericCacheBackendUnitTestBase {
    *   A new MemoryBackend object.
    */
   protected function createCacheBackend($bin) {
-    return new PhpBackend($bin);
+    $backend = new PhpBackend($bin, \Drupal::service('cache_tags.checksum'));
+    return $backend;
   }
 
 }
diff --git a/core/modules/toolbar/src/Tests/ToolbarAdminMenuTest.php b/core/modules/toolbar/src/Tests/ToolbarAdminMenuTest.php
index d0c0685..7b612ed 100644
--- a/core/modules/toolbar/src/Tests/ToolbarAdminMenuTest.php
+++ b/core/modules/toolbar/src/Tests/ToolbarAdminMenuTest.php
@@ -267,7 +267,7 @@ function testCacheClearByCacheTag() {
 
     // Log in admin_user and clear the caches for this user using a tag.
     $this->drupalLogin($this->admin_user);
-    Cache::deleteTags(array('user:' . $admin_user_id));
+    Cache::invalidateTags(array('user:' . $admin_user_id));
 
     // Assert that no toolbar cache exists for admin_user against the
     // language "en".
diff --git a/core/modules/views/src/ViewsData.php b/core/modules/views/src/ViewsData.php
index 796e94b..c90c69e 100644
--- a/core/modules/views/src/ViewsData.php
+++ b/core/modules/views/src/ViewsData.php
@@ -322,6 +322,6 @@ public function clear() {
     $this->storage = array();
     $this->allStorage = array();
     $this->fullyLoaded = FALSE;
-    Cache::deleteTags(array('views_data'));
+    Cache::invalidateTags(array('views_data'));
   }
 }
diff --git a/core/modules/views/tests/src/Unit/ViewsDataTest.php b/core/modules/views/tests/src/Unit/ViewsDataTest.php
index 0476ffd..9002420 100644
--- a/core/modules/views/tests/src/Unit/ViewsDataTest.php
+++ b/core/modules/views/tests/src/Unit/ViewsDataTest.php
@@ -26,6 +26,13 @@ class ViewsDataTest extends UnitTestCase {
   protected $cacheBackend;
 
   /**
+   * The mocked cache tags invalidator.
+   *
+   * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $cacheTagsInvalidator;
+
+  /**
    * The mocked module handler.
    *
    * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
@@ -57,8 +64,9 @@ class ViewsDataTest extends UnitTestCase {
    * {@inheritdoc}
    */
   protected function setUp() {
+    $this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface');
     $this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
-    $this->getContainerWithCacheBins($this->cacheBackend);
+    $this->getContainerWithCacheTagsInvalidator($this->cacheTagsInvalidator);
 
     $configs = array();
     $configs['views.settings']['skip_cache'] = FALSE;
@@ -250,20 +258,21 @@ public function testFullAndTableGetCache() {
     $this->cacheBackend->expects($this->at(3))
       ->method('set')
       ->with("views_data:$random_table_name:en", array());
+    $this->cacheTagsInvalidator->expects($this->once())
+      ->method('invalidateTags')
+      ->with(['views_data']);
     $this->cacheBackend->expects($this->at(4))
-      ->method('deleteAll');
+      ->method('get')
+      ->with("views_data:en")
+      ->will($this->returnValue(FALSE));
     $this->cacheBackend->expects($this->at(5))
-      ->method('get')
-      ->with("views_data:en")
-      ->will($this->returnValue(FALSE));
+      ->method('set')
+      ->with("views_data:en", $expected_views_data);
     $this->cacheBackend->expects($this->at(6))
-      ->method('set')
-      ->with("views_data:en", $expected_views_data);
+      ->method('get')
+      ->with("views_data:$random_table_name:en")
+      ->will($this->returnValue(FALSE));
     $this->cacheBackend->expects($this->at(7))
-      ->method('get')
-      ->with("views_data:$random_table_name:en")
-      ->will($this->returnValue(FALSE));
-    $this->cacheBackend->expects($this->at(8))
       ->method('set')
       ->with("views_data:$random_table_name:en", array());
 
diff --git a/core/modules/views/views.module b/core/modules/views/views.module
index 7c170dc..5c13075 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -472,7 +472,7 @@ function views_field_config_create(FieldConfigInterface $field) {
  * Implements hook_ENTITY_TYPE_update() for 'field_config'.
  */
 function views_field_config_update(FieldConfigInterface $field) {
-  Cache::deleteTags(array('extension' => 'views'));
+  Cache::invalidateTags(array('extension' => 'views'));
   \Drupal::cache('render')->deleteAll();
 }
 
@@ -480,7 +480,7 @@ function views_field_config_update(FieldConfigInterface $field) {
  * Implements hook_ENTITY_TYPE_delete() for 'field_config'.
  */
 function views_field_config_delete(FieldConfigInterface $field) {
-  Cache::deleteTags(array('extension' => 'views'));
+  Cache::invalidateTags(array('extension' => 'views'));
   \Drupal::cache('render')->deleteAll();
 }
 
@@ -489,7 +489,7 @@ function views_field_config_delete(FieldConfigInterface $field) {
  */
 function views_invalidate_cache() {
   // Clear Views' info cache entries.
-  Cache::deleteTags(array('extension' => 'views'));
+  Cache::invalidateTags(array('extension' => 'views'));
 
   // Set the menu as needed to be rebuilt.
   \Drupal::service('router.builder_indicator')->setRebuildNeeded();
diff --git a/core/tests/Drupal/Tests/Core/Cache/BackendChainImplementationUnitTest.php b/core/tests/Drupal/Tests/Core/Cache/BackendChainImplementationUnitTest.php
index f977695..6ab09c4 100644
--- a/core/tests/Drupal/Tests/Core/Cache/BackendChainImplementationUnitTest.php
+++ b/core/tests/Drupal/Tests/Core/Cache/BackendChainImplementationUnitTest.php
@@ -229,7 +229,7 @@ public function testDeleteTagsPropagation() {
       'Two cache items were created in all backends.');
 
     // Invalidate test_tag of value 1. This should invalidate both entries.
-    $this->chain->deleteTags(array('test_tag:2'));
+    $this->chain->invalidateTags(array('test_tag:2'));
     $this->assertSame(FALSE, $this->firstBackend->get('test_cid_clear1')
       && $this->firstBackend->get('test_cid_clear2')
       && $this->secondBackend->get('test_cid_clear1')
@@ -250,7 +250,7 @@ public function testDeleteTagsPropagation() {
       'Two cache items were created in all backends.');
 
     // Invalidate test_tag of value 1. This should invalidate both entries.
-    $this->chain->deleteTags(array('test_tag:1'));
+    $this->chain->invalidateTags(array('test_tag:1'));
     $this->assertSame(FALSE, $this->firstBackend->get('test_cid_clear1')
       && $this->firstBackend->get('test_cid_clear2')
       && $this->secondBackend->get('test_cid_clear1')
@@ -274,7 +274,7 @@ public function testDeleteTagsPropagation() {
       && $this->thirdBackend->get('test_cid_clear3'),
       'Three cached items were created in all backends.');
 
-    $this->chain->deleteTags(array('test_tag_foo:3'));
+    $this->chain->invalidateTags(array('test_tag_foo:3'));
     $this->assertNotSame(FALSE, $this->firstBackend->get('test_cid_clear1')
       && $this->firstBackend->get('test_cid_clear2')
       && $this->secondBackend->get('test_cid_clear1')
diff --git a/core/tests/Drupal/Tests/Core/Cache/CacheCollectorTest.php b/core/tests/Drupal/Tests/Core/Cache/CacheCollectorTest.php
index cf5b3f1..7e91fcb 100644
--- a/core/tests/Drupal/Tests/Core/Cache/CacheCollectorTest.php
+++ b/core/tests/Drupal/Tests/Core/Cache/CacheCollectorTest.php
@@ -19,9 +19,16 @@ class CacheCollectorTest extends UnitTestCase {
   /**
    * The cache backend that should be used.
    *
-   * @var \PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
    */
-  protected $cache;
+  protected $cacheBackend;
+
+  /**
+   * The cache tags invalidator.
+   *
+   * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $cacheTagsInvalidator;
 
   /**
    * The lock backend that should be used.
@@ -48,12 +55,13 @@ class CacheCollectorTest extends UnitTestCase {
    * {@inheritdoc}
    */
   protected function setUp() {
-    $this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
+    $this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
+    $this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface');
     $this->lock = $this->getMock('Drupal\Core\Lock\LockBackendInterface');
     $this->cid = $this->randomMachineName();
-    $this->collector = new CacheCollectorHelper($this->cid, $this->cache, $this->lock);
+    $this->collector = new CacheCollectorHelper($this->cid, $this->cacheBackend, $this->lock);
 
-    $this->getContainerWithCacheBins($this->cache);
+    $this->getContainerWithCacheTagsInvalidator($this->cacheTagsInvalidator);
   }
 
 
@@ -90,7 +98,7 @@ public function testSetAndGetNull() {
     $key = $this->randomMachineName();
     $value = NULL;
 
-    $this->cache->expects($this->once())
+    $this->cacheBackend->expects($this->once())
       ->method('invalidate')
       ->with($this->cid);
     $this->collector->set($key, $value);
@@ -115,7 +123,7 @@ public function testGetFromCache() {
       'data' => array($key => $value),
       'created' => REQUEST_TIME,
     );
-    $this->cache->expects($this->once())
+    $this->cacheBackend->expects($this->once())
       ->method('get')
       ->with($this->cid)
       ->will($this->returnValue($cache));
@@ -137,7 +145,7 @@ public function testDelete() {
     $this->assertTrue($this->collector->has($key));
     $this->assertEquals($value, $this->collector->get($key));
 
-    $this->cache->expects($this->once())
+    $this->cacheBackend->expects($this->once())
       ->method('invalidate')
       ->with($this->cid);
     $this->collector->delete($key);
@@ -151,7 +159,7 @@ public function testDelete() {
   public function testUpdateCacheNoChanges() {
     $this->lock->expects($this->never())
       ->method('acquire');
-    $this->cache->expects($this->never())
+    $this->cacheBackend->expects($this->never())
       ->method('set');
 
     // Destruct the object to trigger the update data process.
@@ -175,10 +183,10 @@ public function testUpdateCache() {
       ->method('acquire')
       ->with($this->cid . ':Drupal\Core\Cache\CacheCollector')
       ->will($this->returnValue(TRUE));
-    $this->cache->expects($this->once())
+    $this->cacheBackend->expects($this->once())
       ->method('get')
       ->with($this->cid, FALSE);
-    $this->cache->expects($this->once())
+    $this->cacheBackend->expects($this->once())
       ->method('set')
       ->with($this->cid, array($key => $value), Cache::PERMANENT, array());
     $this->lock->expects($this->once())
@@ -205,7 +213,7 @@ public function testUpdateCacheLockFail() {
       ->method('acquire')
       ->with($this->cid . ':Drupal\Core\Cache\CacheCollector')
       ->will($this->returnValue(FALSE));
-    $this->cache->expects($this->never())
+    $this->cacheBackend->expects($this->never())
       ->method('set');
 
     // Destruct the object to trigger the update data process.
@@ -223,12 +231,12 @@ public function testUpdateCacheInvalidatedConflict() {
       'data' => array($key => $value),
       'created' => REQUEST_TIME,
     );
-    $this->cache->expects($this->at(0))
+    $this->cacheBackend->expects($this->at(0))
       ->method('get')
       ->with($this->cid)
       ->will($this->returnValue($cache));
 
-    $this->cache->expects($this->at(1))
+    $this->cacheBackend->expects($this->at(1))
       ->method('invalidate')
       ->with($this->cid);
     $this->collector->set($key, 'new value');
@@ -244,11 +252,11 @@ public function testUpdateCacheInvalidatedConflict() {
       'data' => array($key => $value),
       'created' => REQUEST_TIME + 1,
     );
-    $this->cache->expects($this->at(0))
+    $this->cacheBackend->expects($this->at(0))
       ->method('get')
       ->with($this->cid)
       ->will($this->returnValue($cache));
-    $this->cache->expects($this->once())
+    $this->cacheBackend->expects($this->once())
       ->method('delete')
       ->with($this->cid);
     $this->lock->expects($this->once())
@@ -280,11 +288,11 @@ public function testUpdateCacheMerge() {
       'data' => array('other key' => 'other value'),
       'created' => REQUEST_TIME + 1,
     );
-    $this->cache->expects($this->at(0))
+    $this->cacheBackend->expects($this->at(0))
       ->method('get')
       ->with($this->cid)
       ->will($this->returnValue($cache));
-    $this->cache->expects($this->once())
+    $this->cacheBackend->expects($this->once())
       ->method('set')
       ->with($this->cid, array('other key' => 'other value', $key => $value), Cache::PERMANENT, array());
     $this->lock->expects($this->once())
@@ -306,7 +314,7 @@ public function testUpdateCacheDelete() {
       'data' => array($key => $value),
       'created' => REQUEST_TIME,
     );
-    $this->cache->expects($this->at(0))
+    $this->cacheBackend->expects($this->at(0))
       ->method('get')
       ->with($this->cid)
       ->will($this->returnValue($cache));
@@ -322,10 +330,10 @@ public function testUpdateCacheDelete() {
       ->will($this->returnValue(TRUE));
     // The second argument is set to TRUE because we triggered a cache
     // invalidation.
-    $this->cache->expects($this->at(0))
+    $this->cacheBackend->expects($this->at(0))
       ->method('get')
       ->with($this->cid, TRUE);
-    $this->cache->expects($this->once())
+    $this->cacheBackend->expects($this->once())
       ->method('set')
       ->with($this->cid, array(), Cache::PERMANENT, array());
     $this->lock->expects($this->once())
@@ -373,11 +381,11 @@ public function testUpdateCacheClear() {
     $this->assertEquals(1, $this->collector->getCacheMisses());
 
     // Clear the collected cache, should call it again.
-    $this->cache->expects($this->once())
+    $this->cacheBackend->expects($this->once())
       ->method('delete')
       ->with($this->cid);
-    $this->cache->expects($this->never())
-      ->method('deleteTags');
+    $this->cacheTagsInvalidator->expects($this->never())
+      ->method('invalidateTags');
     $this->collector->clear();
     $this->assertEquals($value, $this->collector->get($key));
     $this->assertEquals(2, $this->collector->getCacheMisses());
@@ -390,7 +398,7 @@ public function testUpdateCacheClearTags() {
     $key = $this->randomMachineName();
     $value = $this->randomMachineName();
     $tags = array($this->randomMachineName());
-    $this->collector = new CacheCollectorHelper($this->cid, $this->cache, $this->lock, $tags);
+    $this->collector = new CacheCollectorHelper($this->cid, $this->cacheBackend, $this->lock, $tags);
 
     // Set the data and request it.
     $this->collector->setCacheMissData($key, $value);
@@ -401,10 +409,10 @@ public function testUpdateCacheClearTags() {
     $this->assertEquals(1, $this->collector->getCacheMisses());
 
     // Clear the collected cache using the tags, should call it again.
-    $this->cache->expects($this->never())
+    $this->cacheBackend->expects($this->never())
       ->method('delete');
-    $this->cache->expects($this->once())
-      ->method('deleteTags')
+    $this->cacheTagsInvalidator->expects($this->once())
+      ->method('invalidateTags')
       ->with($tags);
     $this->collector->clear();
     $this->assertEquals($value, $this->collector->get($key));
diff --git a/core/tests/Drupal/Tests/Core/Cache/CacheTagsInvalidatorTest.php b/core/tests/Drupal/Tests/Core/Cache/CacheTagsInvalidatorTest.php
new file mode 100644
index 0000000..adfb7a3
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Cache/CacheTagsInvalidatorTest.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Cache\CacheTest.
+ */
+
+namespace Drupal\Tests\Core\Cache;
+
+use Drupal\Core\Cache\CacheTagsInvalidator;
+use Drupal\Core\DependencyInjection\Container;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Cache\CacheTagsInvalidator
+ * @group Cache
+ */
+class CacheTagsInvalidatorTest extends UnitTestCase {
+
+
+  /**
+   * @covers ::invalidateTags
+   *
+   * @expectedException \LogicException
+   * @expectedExceptionMessage Cache tags must be strings, array given.
+   */
+  public function testInvalidateTagsWithInvalidTags() {
+    $cache_tags_invalidator = new CacheTagsInvalidator();
+    $cache_tags_invalidator->invalidateTags(['node' => [2, 3, 5, 8, 13]]);
+  }
+
+  /**
+   * @covers ::invalidateTags
+   * @covers ::addInvalidator
+   * @covers ::getBins
+   */
+  public function testInvalidateTags() {
+    $cache_tags_invalidator = new CacheTagsInvalidator();
+
+    // This does not actually implement,
+    // \Drupal\Cache\Cache\CacheBackendInterface but we can not mock from two
+    // interfaces, we would need a test class for that.
+    $invalidator_cache_bin = $this->getMock('\Drupal\Core\Cache\CacheTagsInvalidator');
+    $invalidator_cache_bin->expects($this->once())
+      ->method('invalidateTags')
+      ->with(array('node:1'));
+
+    // We do not have to define that invalidateTags() is never called as the
+    // interface does not define that method, trying to call it would result in
+    // a fatal error.
+    $non_invalidator_cache_bin = $this->getMock('\Drupal\Core\Cache\CacheBackendI');
+
+    $container = new Container();
+    $container->set('cache.invalidator_cache_bin', $invalidator_cache_bin);
+    $container->set('cache.non_invalidator_cache_bin', $non_invalidator_cache_bin);
+    $container->setParameter('cache_bins', array('cache.invalidator_cache_bin' => 'invalidator_cache_bin', 'cache.non_invalidator_cache_bin' => 'non_invalidator_cache_bin'));
+    $cache_tags_invalidator->setContainer($container);
+
+    $invalidator = $this->getMock('\Drupal\Core\Cache\CacheTagsInvalidator');
+    $invalidator->expects($this->once())
+      ->method('invalidateTags')
+      ->with(array('node:1'));
+
+    $cache_tags_invalidator->addInvalidator($invalidator);
+
+    $cache_tags_invalidator->invalidateTags(array('node:1'));
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Cache/CacheTest.php b/core/tests/Drupal/Tests/Core/Cache/CacheTest.php
index a425572..ea30f1a 100644
--- a/core/tests/Drupal/Tests/Core/Cache/CacheTest.php
+++ b/core/tests/Drupal/Tests/Core/Cache/CacheTest.php
@@ -117,24 +117,4 @@ public function testBuildTags($prefix, array $suffixes, array $expected) {
     $this->assertEquals($expected, Cache::buildTags($prefix, $suffixes));
   }
 
-  /**
-   * @covers ::deleteTags
-   *
-   * @expectedException \LogicException
-   * @expectedExceptionMessage Cache tags must be strings, array given.
-   */
-  public function testDeleteTags() {
-    Cache::deleteTags(['node' => [2, 3, 5, 8, 13]]);
-  }
-
-  /**
-   * @covers ::invalidateTags
-   *
-   * @expectedException \LogicException
-   * @expectedExceptionMessage Cache tags must be strings, array given.
-   */
-  public function testInvalidateTags() {
-    Cache::invalidateTags(['node' => [2, 3, 5, 8, 13]]);
-  }
-
 }
diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php
index 2da84fe..0480f52 100644
--- a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php
+++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php
@@ -79,9 +79,9 @@ class ConfigEntityBaseUnitTest extends UnitTestCase {
   /**
    * The mocked cache backend.
    *
-   * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\PHPUnit_Framework_MockObject_MockObject
    */
-  protected $cacheBackend;
+  protected $cacheTagsInvalidator;
 
   /**
    * The mocked typed config manager.
@@ -121,7 +121,7 @@ protected function setUp() {
       ->with('en')
       ->will($this->returnValue(new Language(array('id' => 'en'))));
 
-    $this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
+    $this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface');
 
     $this->typedConfigManager = $this->getMock('Drupal\Core\Config\TypedConfigManagerInterface');
 
@@ -129,9 +129,8 @@ protected function setUp() {
     $container->set('entity.manager', $this->entityManager);
     $container->set('uuid', $this->uuid);
     $container->set('language_manager', $this->languageManager);
-    $container->set('cache.test', $this->cacheBackend);
+    $container->set('cache_tags.invalidator', $this->cacheTagsInvalidator);
     $container->set('config.typed', $this->typedConfigManager);
-    $container->setParameter('cache_bins', array('cache.test' => 'test'));
     \Drupal::setContainer($container);
 
     $this->entity = $this->getMockForAbstractClass('\Drupal\Core\Config\Entity\ConfigEntityBase', array($values, $this->entityTypeId));
@@ -361,7 +360,7 @@ public function testEnable() {
    * @depends testSetStatus
    */
   public function testDisable() {
-    $this->cacheBackend->expects($this->once())
+    $this->cacheTagsInvalidator->expects($this->once())
       ->method('invalidateTags')
       ->with(array($this->entityTypeId . ':' . $this->id));
 
diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
index ddfbd81..24ce7d2 100644
--- a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
@@ -85,9 +85,9 @@ class ConfigEntityStorageTest extends UnitTestCase {
   /**
    * The mocked cache backend.
    *
-   * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\PHPUnit_Framework_MockObject_MockObject
    */
-  protected $cacheBackend;
+  protected $cacheTagsInvalidator;
 
   /**
    * The mocked typed config manager.
@@ -153,7 +153,7 @@ protected function setUp() {
       ->with('test_entity_type')
       ->will($this->returnValue($this->entityType));
 
-    $this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
+    $this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface');
 
     $this->typedConfigManager = $this->getMock('Drupal\Core\Config\TypedConfigManagerInterface');
     $this->typedConfigManager->expects($this->any())
@@ -162,8 +162,7 @@ protected function setUp() {
     $container = new ContainerBuilder();
     $container->set('entity.manager', $this->entityManager);
     $container->set('config.typed', $this->typedConfigManager);
-    $container->set('cache.test', $this->cacheBackend);
-    $container->setParameter('cache_bins', array('cache.test' => 'test'));
+    $container->set('cache_tags.invalidator', $this->cacheTagsInvalidator);
     \Drupal::setContainer($container);
 
   }
@@ -173,7 +172,7 @@ protected function setUp() {
    * @covers ::doCreate
    */
   public function testCreateWithPredefinedUuid() {
-    $this->cacheBackend->expects($this->never())
+    $this->cacheTagsInvalidator->expects($this->never())
       ->method('invalidateTags');
 
     $this->moduleHandler->expects($this->at(0))
@@ -198,7 +197,7 @@ public function testCreateWithPredefinedUuid() {
    * @return \Drupal\Core\Entity\EntityInterface
    */
   public function testCreate() {
-    $this->cacheBackend->expects($this->never())
+    $this->cacheTagsInvalidator->expects($this->never())
       ->method('invalidateTags');
 
     $this->moduleHandler->expects($this->at(0))
@@ -240,7 +239,7 @@ public function testSaveInsert(EntityInterface $entity) {
     $config_object->expects($this->once())
       ->method('save');
 
-    $this->cacheBackend->expects($this->once())
+    $this->cacheTagsInvalidator->expects($this->once())
       ->method('invalidateTags')
       ->with(array(
         $this->entityTypeId . '_list', // List cache tag.
@@ -299,7 +298,7 @@ public function testSaveUpdate(EntityInterface $entity) {
     $config_object->expects($this->once())
       ->method('save');
 
-    $this->cacheBackend->expects($this->once())
+    $this->cacheTagsInvalidator->expects($this->once())
       ->method('invalidateTags')
       ->with(array(
         $this->entityTypeId . ':foo', // Own cache tag.
@@ -359,7 +358,7 @@ public function testSaveRename(ConfigEntityInterface $entity) {
     $config_object->expects($this->once())
       ->method('save');
 
-    $this->cacheBackend->expects($this->once())
+    $this->cacheTagsInvalidator->expects($this->once())
       ->method('invalidateTags')
       ->with(array(
         $this->entityTypeId .':bar', // Own cache tag.
@@ -403,7 +402,7 @@ public function testSaveRename(ConfigEntityInterface $entity) {
    * @expectedExceptionMessage The entity does not have an ID.
    */
   public function testSaveInvalid() {
-    $this->cacheBackend->expects($this->never())
+    $this->cacheTagsInvalidator->expects($this->never())
       ->method('invalidateTags');
 
     $entity = $this->getMockEntity();
@@ -428,7 +427,7 @@ public function testSaveDuplicate() {
     $config_object->expects($this->never())
       ->method('save');
 
-    $this->cacheBackend->expects($this->never())
+    $this->cacheTagsInvalidator->expects($this->never())
       ->method('invalidateTags');
 
     $this->configFactory->expects($this->once())
@@ -459,7 +458,7 @@ public function testSaveMismatch() {
     $config_object->expects($this->never())
       ->method('save');
 
-    $this->cacheBackend->expects($this->never())
+    $this->cacheTagsInvalidator->expects($this->never())
       ->method('invalidateTags');
 
     $this->configFactory->expects($this->once())
@@ -492,7 +491,7 @@ public function testSaveNoMismatch() {
     $config_object->expects($this->once())
       ->method('save');
 
-    $this->cacheBackend->expects($this->once())
+    $this->cacheTagsInvalidator->expects($this->once())
       ->method('invalidateTags')
       ->with(array(
         $this->entityTypeId . '_list', // List cache tag.
@@ -542,7 +541,7 @@ public function testSaveChangedUuid() {
         array('id', 'foo'),
       )));
 
-    $this->cacheBackend->expects($this->never())
+    $this->cacheTagsInvalidator->expects($this->never())
       ->method('invalidateTags');
 
     $this->configFactory->expects($this->at(1))
@@ -699,7 +698,7 @@ public function testLoadRevision() {
    * @covers ::deleteRevision
    */
   public function testDeleteRevision() {
-    $this->cacheBackend->expects($this->never())
+    $this->cacheTagsInvalidator->expects($this->never())
       ->method('invalidateTags');
 
     $this->assertSame(NULL, $this->entityStorage->deleteRevision(1));
@@ -725,7 +724,7 @@ public function testDelete() {
       $config_map[] = array("the_config_prefix.$id", $config_object);
     }
 
-    $this->cacheBackend->expects($this->once())
+    $this->cacheTagsInvalidator->expects($this->once())
       ->method('invalidateTags')
       ->with(array(
         $this->entityTypeId . ':bar', // Own cache tag.
@@ -775,7 +774,7 @@ public function testDeleteNothing() {
     $this->configFactory->expects($this->never())
       ->method('get');
 
-    $this->cacheBackend->expects($this->never())
+    $this->cacheTagsInvalidator->expects($this->never())
       ->method('invalidateTags');
 
     $this->entityStorage->delete(array());
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
index 933b7ed..64f2603 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
@@ -63,7 +63,14 @@ class EntityManagerTest extends UnitTestCase {
    *
    * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
    */
-  protected $cache;
+  protected $cacheBackend;
+
+  /**
+   * The cache tags invalidator.
+   *
+   * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $cacheTagsInvalidator;
 
   /**
    * The language manager.
@@ -119,7 +126,8 @@ protected function setUp() {
       ->with('entity_type_build')
       ->will($this->returnValue(array()));
 
-    $this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
+    $this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
+    $this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface');
 
     $language = $this->getMock('Drupal\Core\Language\LanguageInterface');
     $language->expects($this->any())
@@ -139,7 +147,7 @@ protected function setUp() {
     $this->formBuilder = $this->getMock('Drupal\Core\Form\FormBuilderInterface');
     $this->controllerResolver = $this->getClassResolverStub();
 
-    $this->container = $this->getContainerWithCacheBins($this->cache);
+    $this->container = $this->getContainerWithCacheTagsInvalidator($this->cacheTagsInvalidator);
 
     $this->discovery = $this->getMock('Drupal\Component\Plugin\Discovery\DiscoveryInterface');
 
@@ -180,7 +188,7 @@ protected function setUpEntityManager($definitions = array()) {
       ->method('getDefinitions')
       ->will($this->returnValue($definitions));
 
-    $this->entityManager = new TestEntityManager(new \ArrayObject(), $this->moduleHandler, $this->cache, $this->languageManager, $this->translationManager, $this->getClassResolverStub(), $this->typedDataManager, $this->installedDefinitions, $this->eventDispatcher);
+    $this->entityManager = new TestEntityManager(new \ArrayObject(), $this->moduleHandler, $this->cacheBackend, $this->languageManager, $this->translationManager, $this->getClassResolverStub(), $this->typedDataManager, $this->installedDefinitions, $this->eventDispatcher);
     $this->entityManager->setContainer($this->container);
     $this->entityManager->setDiscovery($this->discovery);
   }
@@ -193,14 +201,14 @@ protected function setUpEntityManager($definitions = array()) {
    */
   public function testClearCachedDefinitions() {
     $this->setUpEntityManager();
-    $this->cache->expects($this->at(0))
-      ->method('deleteTags')
+    $this->cacheTagsInvalidator->expects($this->at(0))
+      ->method('invalidateTags')
       ->with(array('entity_types'));
-    $this->cache->expects($this->at(1))
-      ->method('deleteTags')
+    $this->cacheTagsInvalidator->expects($this->at(1))
+      ->method('invalidateTags')
       ->with(array('entity_bundles'));
-    $this->cache->expects($this->at(2))
-      ->method('deleteTags')
+    $this->cacheTagsInvalidator->expects($this->at(2))
+      ->method('invalidateTags')
       ->with(array('entity_field_info'));
 
     $this->entityManager->clearCachedDefinitions();
@@ -524,21 +532,21 @@ public function testGetBaseFieldDefinitionsWithCaching() {
 
     $expected = array('id' => $field_definition);
 
-    $this->cache->expects($this->at(0))
+    $this->cacheBackend->expects($this->at(0))
       ->method('get')
       ->with('entity_base_field_definitions:test_entity_type:en', FALSE)
       ->will($this->returnValue(FALSE));
-    $this->cache->expects($this->at(1))
+    $this->cacheBackend->expects($this->at(1))
       ->method('get')
       ->with('entity_type', FALSE)
       ->will($this->returnValue(FALSE));
-    $this->cache->expects($this->at(2))
+    $this->cacheBackend->expects($this->at(2))
       ->method('set')
       ->with('entity_type');
-    $this->cache->expects($this->at(3))
+    $this->cacheBackend->expects($this->at(3))
       ->method('set')
       ->with('entity_base_field_definitions:test_entity_type:en');
-    $this->cache->expects($this->at(4))
+    $this->cacheBackend->expects($this->at(4))
       ->method('get')
       ->with('entity_base_field_definitions:test_entity_type:en', FALSE)
       ->will($this->returnValue((object) array('data' => $expected)));
@@ -558,27 +566,27 @@ public function testGetFieldDefinitionsWithCaching() {
 
     $expected = array('id' => $field_definition);
 
-    $this->cache->expects($this->at(0))
+    $this->cacheBackend->expects($this->at(0))
       ->method('get')
       ->with('entity_base_field_definitions:test_entity_type:en', FALSE)
       ->will($this->returnValue((object) array('data' => $expected)));
-    $this->cache->expects($this->at(1))
+    $this->cacheBackend->expects($this->at(1))
       ->method('get')
       ->with('entity_bundle_field_definitions:test_entity_type:test_bundle:en', FALSE)
       ->will($this->returnValue(FALSE));
-    $this->cache->expects($this->at(2))
+    $this->cacheBackend->expects($this->at(2))
       ->method('get')
       ->with('entity_type', FALSE)
       ->will($this->returnValue(FALSE));
-    $this->cache->expects($this->at(3))
+    $this->cacheBackend->expects($this->at(3))
       ->method('set');
-    $this->cache->expects($this->at(4))
+    $this->cacheBackend->expects($this->at(4))
       ->method('set');
-    $this->cache->expects($this->at(5))
+    $this->cacheBackend->expects($this->at(5))
       ->method('get')
       ->with('entity_base_field_definitions:test_entity_type:en', FALSE)
       ->will($this->returnValue((object) array('data' => $expected)));
-    $this->cache->expects($this->at(6))
+    $this->cacheBackend->expects($this->at(6))
       ->method('get')
       ->with('entity_bundle_field_definitions:test_entity_type:test_bundle:en', FALSE)
       ->will($this->returnValue((object) array('data' => $expected)));
@@ -617,29 +625,29 @@ public function testGetFieldStorageDefinitionsWithCaching() {
       'field_storage' => $field_storage_definition,
     );
 
-    $this->cache->expects($this->at(0))
+    $this->cacheBackend->expects($this->at(0))
       ->method('get')
       ->with('entity_base_field_definitions:test_entity_type:en', FALSE)
       ->will($this->returnValue((object) array('data' => array('id' => $expected['id']))));
-    $this->cache->expects($this->at(1))
+    $this->cacheBackend->expects($this->at(1))
       ->method('get')
       ->with('entity_field_storage_definitions:test_entity_type:en', FALSE)
       ->will($this->returnValue(FALSE));
-    $this->cache->expects($this->at(2))
+    $this->cacheBackend->expects($this->at(2))
       ->method('get')
       ->with('entity_type', FALSE)
       ->will($this->returnValue(FALSE));
-    $this->cache->expects($this->at(3))
+    $this->cacheBackend->expects($this->at(3))
       ->method('set')
       ->with('entity_type');
-    $this->cache->expects($this->at(4))
+    $this->cacheBackend->expects($this->at(4))
       ->method('set')
       ->with('entity_field_storage_definitions:test_entity_type:en');
-    $this->cache->expects($this->at(5))
+    $this->cacheBackend->expects($this->at(5))
       ->method('get')
       ->with('entity_base_field_definitions:test_entity_type:en', FALSE)
       ->will($this->returnValue((object) array('data' => array('id' => $expected['id']))));
-    $this->cache->expects($this->at(6))
+    $this->cacheBackend->expects($this->at(6))
       ->method('get')
       ->with('entity_field_storage_definitions:test_entity_type:en', FALSE)
       ->will($this->returnValue((object) array('data' => $expected)));
@@ -771,8 +779,8 @@ protected function setUpEntityWithFieldDefinition($custom_invoke_all = FALSE, $f
    */
   public function testClearCachedFieldDefinitions() {
     $this->setUpEntityManager();
-    $this->cache->expects($this->once())
-      ->method('deleteTags')
+    $this->cacheTagsInvalidator->expects($this->once())
+      ->method('invalidateTags')
       ->with(array('entity_field_info'));
     $this->typedDataManager->expects($this->once())
       ->method('clearCachedDefinitions');
@@ -787,8 +795,8 @@ public function testClearCachedFieldDefinitions() {
    */
   public function testClearCachedBundles() {
     $this->setUpEntityManager();
-    $this->cache->expects($this->once())
-      ->method('deleteTags')
+    $this->cacheTagsInvalidator->expects($this->once())
+      ->method('invalidateTags')
       ->with(array('entity_bundles'));
 
     $this->entityManager->clearCachedBundles();
@@ -859,30 +867,30 @@ public function testGetAllBundleInfo() {
       'apple' => $apple,
       'banana' => $banana,
     ));
-    $this->cache->expects($this->at(0))
+    $this->cacheBackend->expects($this->at(0))
       ->method('get')
       ->with("entity_bundle_info:en", FALSE)
       ->will($this->returnValue(FALSE));
-    $this->cache->expects($this->at(1))
+    $this->cacheBackend->expects($this->at(1))
       ->method('get')
       ->with("entity_type", FALSE)
       ->will($this->returnValue(FALSE));
-    $this->cache->expects($this->at(2))
+    $this->cacheBackend->expects($this->at(2))
       ->method('set')
       ->with("entity_type");
-    $this->cache->expects($this->at(3))
+    $this->cacheBackend->expects($this->at(3))
       ->method('set')
       ->with("entity_bundle_info:en");
-    $this->cache->expects($this->at(4))
-      ->method('deleteTags')
+    $this->cacheTagsInvalidator->expects($this->at(0))
+      ->method('invalidateTags')
       ->with(array('entity_types'));
-    $this->cache->expects($this->at(5))
-      ->method('deleteTags')
+    $this->cacheTagsInvalidator->expects($this->at(1))
+      ->method('invalidateTags')
       ->with(array('entity_bundles'));
-    $this->cache->expects($this->at(6))
-      ->method('deleteTags')
+    $this->cacheTagsInvalidator->expects($this->at(2))
+      ->method('invalidateTags')
       ->with(array('entity_field_info'));
-    $this->cache->expects($this->at(7))
+    $this->cacheBackend->expects($this->at(4))
       ->method('get')
       ->with("entity_bundle_info:en", FALSE)
       ->will($this->returnValue((object) array('data' => 'cached data')));
@@ -1015,7 +1023,7 @@ function testgetExtraFields() {
       ->method('getCurrentLanguage')
       ->will($this->returnValue($language));
 
-    $this->cache->expects($this->once())
+    $this->cacheBackend->expects($this->once())
       ->method('get')
       ->with($cache_id);
 
@@ -1027,7 +1035,7 @@ function testgetExtraFields() {
       ->method('alter')
       ->with('entity_extra_field_info', $hook_bundle_extra_fields);
 
-    $this->cache->expects($this->once())
+    $this->cacheBackend->expects($this->once())
       ->method('set')
       ->with($cache_id, $processed_hook_bundle_extra_fields[$entity_type_id][$bundle]);
 
@@ -1167,7 +1175,7 @@ public function testGetFieldMapFromCache() {
       )
     );
     $this->setUpEntityManager();
-    $this->cache->expects($this->once())
+    $this->cacheBackend->expects($this->once())
       ->method('get')
       ->with('entity_field_map')
       ->will($this->returnValue((object) array('data' => $expected)));
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php
index 6535732..3f4029d 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php
@@ -73,11 +73,11 @@ class EntityUnitTest extends UnitTestCase {
   protected $languageManager;
 
   /**
-   * The mocked cache backend.
+   * The mocked cache tags invalidator.
    *
-   * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\PHPUnit_Framework_MockObject_MockObject
    */
-  protected $cacheBackend;
+  protected $cacheTagsInvalidator;
 
   /**
    * The entity values.
@@ -116,14 +116,13 @@ protected function setUp() {
       ->with('en')
       ->will($this->returnValue(new Language(array('id' => 'en'))));
 
-    $this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
+    $this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidator');
 
     $container = new ContainerBuilder();
     $container->set('entity.manager', $this->entityManager);
     $container->set('uuid', $this->uuid);
     $container->set('language_manager', $this->languageManager);
-    $container->set('cache.test', $this->cacheBackend);
-    $container->setParameter('cache_bins', array('cache.test' => 'test'));
+    $container->set('cache_tags.invalidator', $this->cacheTagsInvalidator);
     \Drupal::setContainer($container);
 
     $this->entity = $this->getMockForAbstractClass('\Drupal\Core\Entity\Entity', array($this->values, $this->entityTypeId));
@@ -393,12 +392,12 @@ public function testPreSave() {
    * @covers ::postSave
    */
   public function testPostSave() {
-    $this->cacheBackend->expects($this->at(0))
+    $this->cacheTagsInvalidator->expects($this->at(0))
       ->method('invalidateTags')
       ->with(array(
         $this->entityTypeId . '_list', // List cache tag.
       ));
-    $this->cacheBackend->expects($this->at(1))
+    $this->cacheTagsInvalidator->expects($this->at(1))
       ->method('invalidateTags')
       ->with(array(
         $this->entityTypeId . ':' . $this->values['id'], // Own cache tag.
@@ -450,7 +449,7 @@ public function testPreDelete() {
    * @covers ::postDelete
    */
   public function testPostDelete() {
-    $this->cacheBackend->expects($this->once())
+    $this->cacheTagsInvalidator->expects($this->once())
       ->method('invalidateTags')
       ->with(array(
         $this->entityTypeId . ':' . $this->values['id'],
diff --git a/core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php
index ee10e54..79c5999 100644
--- a/core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php
@@ -68,11 +68,11 @@ class KeyValueEntityStorageTest extends UnitTestCase {
   protected $entityManager;
 
   /**
-   * The mocked cache backend.
+   * The mocked cache tags invalidator.
    *
-   * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\PHPUnit_Framework_MockObject_MockObject
    */
-  protected $cacheBackend;
+  protected $cacheTagsInvalidator;
 
   /**
    * {@inheritdoc}
@@ -110,7 +110,7 @@ protected function setUpKeyValueEntityStorage($uuid_key = 'uuid') {
       ->with('test_entity_type')
       ->will($this->returnValue($this->entityType));
 
-    $this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
+    $this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface');
 
     $this->keyValueStore = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreInterface');
     $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
@@ -130,8 +130,7 @@ protected function setUpKeyValueEntityStorage($uuid_key = 'uuid') {
     $container = new ContainerBuilder();
     $container->set('entity.manager', $this->entityManager);
     $container->set('language_manager', $this->languageManager);
-    $container->set('cache.test', $this->cacheBackend);
-    $container->setParameter('cache_bins', array('cache.test' => 'test'));
+    $container->set('cache_tags.invalidator', $this->cacheTagsInvalidator);
     \Drupal::setContainer($container);
   }
 
diff --git a/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php b/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php
index 0a58e81..0e7ac2f 100644
--- a/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php
+++ b/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php
@@ -121,8 +121,8 @@ protected function setUp() {
     $logger = $this->getMock('Psr\Log\LoggerInterface');
     $this->themeHandler = new TestThemeHandler($this->root, $this->configFactory, $this->moduleHandler, $this->state, $this->infoParser, $logger, $this->cssCollectionOptimizer, $this->configInstaller, $this->configManager, $this->routeBuilderIndicator, $this->extensionDiscovery);
 
-    $cache_backend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
-    $this->getContainerWithCacheBins($cache_backend);
+    $cache_tags.invalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface');
+    $this->getContainerWithCacheTagsInvalidator($cache_tags_invalidator);
   }
 
   /**
diff --git a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
index c2ef841..9cd9375 100644
--- a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
@@ -190,18 +190,17 @@ public function testDefaultPluginManagerWithFilledCache() {
    */
   public function testCacheClearWithTags() {
     $cid = $this->randomMachineName();
-    $cache_backend = $this->getMockBuilder('Drupal\Core\Cache\MemoryBackend')
-      ->disableOriginalConstructor()
-      ->getMock();
-    $cache_backend
+    $cache_backend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
+    $cache_tags_invalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface');
+    $cache_tags_invalidator
       ->expects($this->once())
-      ->method('deleteTags')
+      ->method('invalidateTags')
       ->with(array('tag'));
     $cache_backend
       ->expects($this->never())
       ->method('deleteMultiple');
 
-    $this->getContainerWithCacheBins($cache_backend);
+    $this->getContainerWithCacheTagsInvalidator($cache_tags_invalidator);
 
     $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions, NULL, NULL, '\Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface');
     $plugin_manager->setCacheBackend($cache_backend, $cid, array('tag'));
diff --git a/core/tests/Drupal/Tests/UnitTestCase.php b/core/tests/Drupal/Tests/UnitTestCase.php
index 43748e8..1a0ac77 100644
--- a/core/tests/Drupal/Tests/UnitTestCase.php
+++ b/core/tests/Drupal/Tests/UnitTestCase.php
@@ -8,8 +8,7 @@
 namespace Drupal\Tests;
 
 use Drupal\Component\Utility\Random;
-use Drupal\Component\Utility\String;
-use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 
 /**
@@ -197,24 +196,20 @@ public function getStringTranslationStub() {
   }
 
   /**
-   * Sets up a container with cache bins.
+   * Sets up a container with a cache tags invalidator.
    *
-   * @param \Drupal\Core\Cache\CacheBackendInterface $backend
-   *   The cache backend to set up.
+   * @param \Drupal\Core\Cache\CacheTagsInvalidatorInterface $cache_tags_validator
+   *   The cache tags invalidator.
    *
    * @return \Symfony\Component\DependencyInjection\ContainerInterface|\PHPUnit_Framework_MockObject_MockObject
-   *   The container with the cache bins set up.
+   *   The container with the cache tags invalidator service.
    */
-  protected function getContainerWithCacheBins(CacheBackendInterface $backend) {
+  protected function getContainerWithCacheTagsInvalidator(CacheTagsInvalidatorInterface $cache_tags_validator) {
     $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
     $container->expects($this->any())
-      ->method('getParameter')
-      ->with('cache_bins')
-      ->will($this->returnValue(array('cache.test' => 'test')));
-    $container->expects($this->any())
       ->method('get')
-      ->with('cache.test')
-      ->will($this->returnValue($backend));
+      ->with('cache_tags.invalidator')
+      ->will($this->returnValue($cache_tags_validator));
 
     \Drupal::setContainer($container);
     return $container;
