diff --git a/core/includes/module.inc b/core/includes/module.inc
index 11f8244..b93c0b6 100644
--- a/core/includes/module.inc
+++ b/core/includes/module.inc
@@ -5,9 +5,11 @@
  * API for loading and interacting with Drupal modules.
  */
 
-use Drupal\Component\Graph\Graph;
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\Yaml\Parser;
 
 /**
  * Builds a list of bootstrap modules and enabled modules and themes.
@@ -566,7 +568,6 @@ function module_uninstall($module_list = array(), $uninstall_dependents = TRUE)
     $module_list = array_keys($module_list);
   }
 
-  $storage = drupal_container()->get('config.storage');
   $schema_store = Drupal::keyValue('system.schema');
   $disabled_config = config('system.module.disabled');
   foreach ($module_list as $module) {
@@ -578,6 +579,38 @@ function module_uninstall($module_list = array(), $uninstall_dependents = TRUE)
     // Remove all configuration belonging to the module.
     config_uninstall_default_config('module', $module);
 
+    // Remove any cache bins defined by the module.
+    $service_yaml_file = drupal_get_path('module', $module) . "/$module.services.yml";
+    if (file_exists($service_yaml_file)) {
+      $parser = new Parser;
+      $definitions = $parser->parse(file_get_contents($service_yaml_file));
+      if (isset($definitions['services'])) {
+        foreach ($definitions['services'] as $id => $definition) {
+          if (isset($definition['tags'])) {
+            foreach ($definition['tags'] as $tag) {
+              // This works for the default cache registration and even in some
+              // cases when a non-default "super" factory is used. That should
+              // be extremely rare.
+              if ($tag['name'] == 'cache.bin' && isset($definition['factory_service']) && isset($definition['factory_method']) && !empty($definition['arguments'])) {
+                try {
+                  $factory = Drupal::service($definition['factory_service']);
+                  if (method_exists($factory, $definition['factory_method'])) {
+                    $backend = call_user_func_array(array($factory, $definition['factory_method']), $definition['arguments']);
+                    if ($backend instanceof CacheBackendInterface) {
+                      $backend->removeBin();
+                    }
+                  }
+                }
+                catch (Exception $e) {
+                  watchdog(WATCHDOG_ERROR, 'Failed to remove cache bin defined by the service %id.', array('%id' => $id));
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+
     watchdog('system', '%module module uninstalled.', array('%module' => $module), WATCHDOG_INFO);
     $schema_store->delete($module);
     $disabled_config->clear($module);
diff --git a/core/lib/Drupal/Core/Cache/BackendChain.php b/core/lib/Drupal/Core/Cache/BackendChain.php
index d3fac6e..d4a8bd1 100644
--- a/core/lib/Drupal/Core/Cache/BackendChain.php
+++ b/core/lib/Drupal/Core/Cache/BackendChain.php
@@ -222,4 +222,13 @@ public function isEmpty() {
 
     return TRUE;
   }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::removeBin().
+   */
+  public function removeBin() {
+    foreach ($this->backends as $backend) {
+      $this->removeBin();
+    }
+  }
 }
diff --git a/core/lib/Drupal/Core/Cache/CacheBackendInterface.php b/core/lib/Drupal/Core/Cache/CacheBackendInterface.php
index d38a094..a054604 100644
--- a/core/lib/Drupal/Core/Cache/CacheBackendInterface.php
+++ b/core/lib/Drupal/Core/Cache/CacheBackendInterface.php
@@ -287,6 +287,11 @@ public function invalidateAll();
   public function garbageCollection();
 
   /**
+   * Remove a cache bin.
+   */
+  public function removeBin();
+
+  /**
    * Checks if a cache bin is empty.
    *
    * A cache bin is considered empty if it does not contain any valid data for
diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackend.php b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
index 39359c3..aada83c 100644
--- a/core/lib/Drupal/Core/Cache/DatabaseBackend.php
+++ b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
@@ -69,7 +69,13 @@ public function getMultiple(&$cids, $allow_invalid = FALSE) {
     // is used here only due to the performance overhead we would incur
     // otherwise. When serving an uncached page, the overhead of using
     // ::select() is a much smaller proportion of the request.
-    $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' => $cids));
+    $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' => $cids));
+    }
+    catch (\Exception $e) {
+      // Nothing to do.
+    }
     $cache = array();
     foreach ($result as $item) {
       $item = $this->prepareItem($item, $allow_invalid);
@@ -134,6 +140,28 @@ protected function prepareItem($cache, $allow_invalid) {
    * Implements Drupal\Core\Cache\CacheBackendInterface::set().
    */
   public function set($cid, $data, $expire = CacheBackendInterface::CACHE_PERMANENT, array $tags = array()) {
+    // This is the first try.
+    $try = 1;
+    // Maximum one tries.
+    $max = 1;
+    do {
+      try {
+        $this->doSet($cid, $data, $expire, $tags);
+      }
+      catch (\Exception $e) {
+        if ($this->ensureBinExists()) {
+          // If the bin was just created, try one more time but only one more.
+          // By using try - max we avoid an infinite loop.
+          $max = 2;
+        }
+      }
+    } while ($try++ < $max);
+  }
+
+  /**
+   * Actually set the cache.
+   */
+  protected function doSet($cid, $data, $expire, $tags) {
     $flat_tags = $this->flattenTags($tags);
     $checksum = $this->checksumTags($flat_tags);
     $fields = array(
@@ -163,9 +191,7 @@ public function set($cid, $data, $expire = CacheBackendInterface::CACHE_PERMANEN
    * Implements Drupal\Core\Cache\CacheBackendInterface::delete().
    */
   public function delete($cid) {
-    $this->connection->delete($this->bin)
-      ->condition('cid', $cid)
-      ->execute();
+    $this->deleteMultiple(array($cid));
   }
 
   /**
@@ -188,11 +214,16 @@ public function deleteTags(array $tags) {
     $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache');
     foreach ($this->flattenTags($tags) as $tag) {
       unset($tag_cache[$tag]);
-      $this->connection->merge('cache_tags')
-        ->insertFields(array('deletions' => 1))
-        ->expression('deletions', 'deletions + 1')
-        ->key(array('tag' => $tag))
-        ->execute();
+      try {
+        $this->connection->merge('cache_tags')
+          ->insertFields(array('deletions' => 1))
+          ->expression('deletions', 'deletions + 1')
+          ->key(array('tag' => $tag))
+          ->execute();
+      }
+      catch (\Exception $e) {
+        $this->catchException($e, 'cache_tags');
+      }
     }
   }
 
@@ -200,7 +231,12 @@ public function deleteTags(array $tags) {
    * Implements Drupal\Core\Cache\CacheBackendInterface::deleteAll().
    */
   public function deleteAll() {
-    $this->connection->truncate($this->bin)->execute();
+    try {
+      $this->connection->truncate($this->bin)->execute();
+    }
+    catch (\Exception $e) {
+      $this->catchException($e);
+    }
   }
 
   /**
@@ -214,28 +250,38 @@ public function invalidate($cid) {
    * Implements Drupal\Core\Cache\CacheBackendInterface::invalideMultiple().
    */
   public function invalidateMultiple(array $cids) {
-    // Update in chunks when a large array is passed.
-    do {
-      $this->connection->update($this->bin)
-        ->fields(array('expire' => REQUEST_TIME - 1))
-        ->condition('cid', array_splice($cids, 0, 1000), 'IN')
-        ->execute();
+    try {
+      // Update in chunks when a large array is passed.
+      do {
+        $this->connection->update($this->bin)
+          ->fields(array('expire' => REQUEST_TIME - 1))
+          ->condition('cid', array_splice($cids, 0, 1000), 'IN')
+          ->execute();
+      }
+      while (count($cids));
+    }
+    catch (\Exception $e) {
+      $this->catchException($e);
     }
-    while (count($cids));
   }
 
   /**
    * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateTags().
    */
   public function invalidateTags(array $tags) {
-    $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache');
-    foreach ($this->flattenTags($tags) as $tag) {
-      unset($tag_cache[$tag]);
-      $this->connection->merge('cache_tags')
-        ->insertFields(array('invalidations' => 1))
-        ->expression('invalidations', 'invalidations + 1')
-        ->key(array('tag' => $tag))
-        ->execute();
+    try {
+      $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache');
+      foreach ($this->flattenTags($tags) as $tag) {
+        unset($tag_cache[$tag]);
+        $this->connection->merge('cache_tags')
+          ->insertFields(array('invalidations' => 1))
+          ->expression('invalidations', 'invalidations + 1')
+          ->key(array('tag' => $tag))
+          ->execute();
+      }
+    }
+    catch (\Exception $e) {
+      $this->catchException($e, 'cache_tags');
     }
   }
 
@@ -243,19 +289,31 @@ public function invalidateTags(array $tags) {
    * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateAll().
    */
   public function invalidateAll() {
-    $this->connection->update($this->bin)
-      ->fields(array('expire' => REQUEST_TIME - 1))
-      ->execute();
+    try {
+      $this->connection->update($this->bin)
+        ->fields(array('expire' => REQUEST_TIME - 1))
+        ->execute();
+    }
+    catch (\Exception $e) {
+      $this->catchException($e);
+    }
   }
 
   /**
    * Implements Drupal\Core\Cache\CacheBackendInterface::garbageCollection().
    */
   public function garbageCollection() {
-    Database::getConnection()->delete($this->bin)
-      ->condition('expire', CacheBackendInterface::CACHE_PERMANENT, '<>')
-      ->condition('expire', REQUEST_TIME, '<')
-      ->execute();
+    try {
+      Database::getConnection()->delete($this->bin)
+        ->condition('expire', CacheBackendInterface::CACHE_PERMANENT, '<>')
+        ->condition('expire', REQUEST_TIME, '<')
+        ->execute();
+    }
+    catch (\Exception $e) {
+      // If the table does not exist, it surely does not have garbage in it.
+      // If the table exists, the next garbage collection will clean up.
+      // There is nothing to do.
+    }
   }
 
   /**
@@ -329,9 +387,159 @@ public function isEmpty() {
     $this->garbageCollection();
     $query = $this->connection->select($this->bin);
     $query->addExpression('1');
-    $result = $query->range(0, 1)
-      ->execute()
-      ->fetchField();
+    try {
+      $result = $query->range(0, 1)
+        ->execute()
+        ->fetchField();
+    }
+    catch (\Exception $e) {
+      $this->catchException($e);
+    }
     return empty($result);
   }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::removeBin().
+   */
+  public function removeBin() {
+    try {
+      $this->connection->schema()->dropTable($this->bin);
+    }
+    catch (\Exception $e) {
+      $this->catchException($e);
+    }
+  }
+
+  /**
+   * Check if the cache bin exists and create it if not.
+   */
+  protected function ensureBinExists() {
+    try {
+      $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('cache_tags')) {
+          $database_schema->createTable($this->bin, $schema_definition['cache_tags']);
+        }
+        return TRUE;
+      }
+    }
+    // If another process has already created the cache table, attempting to
+    // recreate it will throw an exception. In this case just catch the
+    // exception and do nothing.
+    catch (\Exception $e) {
+    }
+    return FALSE;
+  }
+
+  /**
+   * Act on an exception when cache might be stale.
+   *
+   * If the cache_tags 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 cache_tags.
+   */
+  protected function catchException(\Exception $e, $table_name = NULL) {
+    if ($this->connection->schema()->tableExists($table_name ?: $this->bin)) {
+      throw $e;
+    }
+  }
+
+  /**
+   * Defines the schema for the cache bin and cache_tags table.
+   */
+  public function schemaDefinition() {
+    $schema['bin'] = array(
+      'description' => 'Storage for the cache API.',
+      'fields' => array(
+        'cid' => array(
+          'description' => 'Primary Key: Unique cache ID.',
+          'type' => 'varchar',
+          'length' => 255,
+          'not null' => TRUE,
+          'default' => '',
+        ),
+        'data' => array(
+          'description' => 'A collection of data to cache.',
+          'type' => 'blob',
+          'not null' => FALSE,
+          'size' => 'big',
+        ),
+        'expire' => array(
+          'description' => 'A Unix timestamp indicating when the cache entry should expire, or 0 for never.',
+          'type' => 'int',
+          'not null' => TRUE,
+          'default' => 0,
+        ),
+        'created' => array(
+          'description' => 'A Unix timestamp indicating when the cache entry was created.',
+          'type' => 'int',
+          'not null' => TRUE,
+          'default' => 0,
+        ),
+        'serialized' => array(
+          'description' => 'A flag to indicate whether content is serialized (1) or not (0).',
+          'type' => 'int',
+          'size' => 'small',
+          'not null' => TRUE,
+          'default' => 0,
+        ),
+        'tags' => array(
+          'description' => 'Space-separated list of cache tags for this entry.',
+          'type' => 'text',
+          'size' => 'big',
+          'not null' => FALSE,
+        ),
+        'checksum_invalidations' => 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['cache_tags'] = 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/MemoryBackend.php b/core/lib/Drupal/Core/Cache/MemoryBackend.php
index ac83a06..0b37932 100644
--- a/core/lib/Drupal/Core/Cache/MemoryBackend.php
+++ b/core/lib/Drupal/Core/Cache/MemoryBackend.php
@@ -215,4 +215,10 @@ public function isEmpty() {
    */
   public function garbageCollection() {
   }
+
+ /**
+  * Implements Drupal\Core\CacheBackendInterface::removeBin().
+  */
+  public function removeBin() {}
+
 }
diff --git a/core/lib/Drupal/Core/Cache/NullBackend.php b/core/lib/Drupal/Core/Cache/NullBackend.php
index 16c0331..c7e40f5 100644
--- a/core/lib/Drupal/Core/Cache/NullBackend.php
+++ b/core/lib/Drupal/Core/Cache/NullBackend.php
@@ -98,4 +98,9 @@ public function garbageCollection() {}
   public function isEmpty() {
     return TRUE;
   }
+
+  /**
+   * Implements Drupal\Core\CacheBackendInterface::removeBin().
+   */
+  public function removeBin() {}
 }
diff --git a/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php b/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php
index 4fbba3b..73785fd 100644
--- a/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php
+++ b/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php
@@ -480,7 +480,7 @@ public function prepareComment($comment, $length = NULL) {
     // Truncate comment to maximum comment length.
     if (isset($length)) {
       // Add table prefixes before truncating.
-      $comment = truncate_utf8($this->connection->prefixTables($comment), $length, TRUE, TRUE);
+      $comment = substr($this->connection->prefixTables($comment), 0, $length);
     }
 
     return $this->connection->quote($comment);
diff --git a/core/modules/block/block.install b/core/modules/block/block.install
index b1fc925..01c5779 100644
--- a/core/modules/block/block.install
+++ b/core/modules/block/block.install
@@ -7,17 +7,6 @@
 use Drupal\Component\Uuid\Uuid;
 
 /**
- * Implements hook_schema().
- */
-function block_schema() {
-
-  $schema['cache_block'] = drupal_get_schema_unprocessed('system', 'cache');
-  $schema['cache_block']['description'] = 'Cache table for the Block module to store already built blocks, identified by module, delta, and various contexts which may change the block, such as theme, locale, and caching mode defined for the block.';
-
-  return $schema;
-}
-
-/**
  * Implements hook_install().
  */
 function block_install() {
diff --git a/core/modules/field/field.install b/core/modules/field/field.install
index 695fd89..eb1c82f 100644
--- a/core/modules/field/field.install
+++ b/core/modules/field/field.install
@@ -161,9 +161,6 @@ function field_schema() {
       'deleted' => array('deleted'),
     ),
   );
-  $schema['cache_field'] = drupal_get_schema_unprocessed('system', 'cache');
-  $schema['cache_field']['description'] = 'Cache table for the Field module to store already built field informations.';
-
   return $schema;
 }
 
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 68127e2..35ff3e7 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -1934,12 +1934,12 @@ function hook_mail($key, &$message, $params) {
 /**
  * Flush all persistent and static caches.
  *
- * This hook asks your module to clear all of its persistent (database) and
- * static caches, in order to ensure a clean environment for subsequently
+ * This hook asks your module to clear all of its static caches,
+ * in order to ensure a clean environment for subsequently
  * invoked data rebuilds.
  *
  * Do NOT use this hook for rebuilding information. Only use it to flush custom
- * caches and return the names of additional cache bins to flush.
+ * caches.
  *
  * Static caches using drupal_static() do not need to be reset manually.
  * However, all other static variables that do not use drupal_static() must be
diff --git a/core/modules/views/views.install b/core/modules/views/views.install
index 8bda10e..aea8d0c 100644
--- a/core/modules/views/views.install
+++ b/core/modules/views/views.install
@@ -15,19 +15,6 @@ function views_install() {
 }
 
 /**
- * Implements hook_schema().
- */
-function views_schema() {
-  $schema['cache_views_info'] = drupal_get_schema_unprocessed('system', 'cache');
-
-  $schema['cache_views_results'] = drupal_get_schema_unprocessed('system', 'cache');
-  $schema['cache_views_results']['description'] = 'Cache table for views to store pre-rendered queries, results, and display output.';
-  $schema['cache_views_results']['fields']['serialized']['default'] = 1;
-
-  return $schema;
-}
-
-/**
  * Provide an initial schema.
  *
  * @see update_module_enable().
