diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index d8f5e2f..d5b411e 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -321,6 +321,7 @@ function install_begin_request(&$install_state) {
   drupal_language_initialize();
 
   require_once DRUPAL_ROOT . '/core/includes/ajax.inc';
+  require_once DRUPAL_ROOT . '/core/includes/cache.inc';
   // Override the module list with a minimal set of modules.
   $module_list['system']['filename'] = 'core/modules/system/system.module';
   $module_list['user']['filename']   = 'core/modules/user/user.module';
@@ -328,18 +329,6 @@ function install_begin_request(&$install_state) {
   drupal_load('module', 'system');
   drupal_load('module', 'user');
 
-  // Load the cache infrastructure using a "fake" cache implementation that
-  // does not attempt to write to the database. We need this during the initial
-  // part of the installer because the database is not available yet. We
-  // continue to use it even when the database does become available, in order
-  // to preserve consistency between interactive and command-line installations
-  // (the latter complete in one page request and therefore are forced to
-  // continue using the cache implementation they started with) and also
-  // because any data put in the cache during the installer is inherently
-  // suspect, due to the fact that Drupal is not fully set up yet.
-  require_once DRUPAL_ROOT . '/core/includes/cache.inc';
-  $conf['cache_classes'] = array('cache' => 'Drupal\Core\Cache\InstallBackend');
-
   // The install process cannot use the database lock backend since the database
   // is not fully up, so we use a null backend implementation during the
   // installation process. This will also speed up the installation process.
diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackend.php b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
index d4de8bf..db594d9 100644
--- a/core/lib/Drupal/Core/Cache/DatabaseBackend.php
+++ b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
@@ -41,6 +41,14 @@ class DatabaseBackend implements CacheBackendInterface {
   }
 
   /**
+   * Checks whether the database is available.
+   */
+  protected function isDatabaseAvailable() {
+    global $install_state;
+    return class_exists('Drupal\Core\Database\Database', FALSE) && (empty($install_state) || !empty($install_state['database_tables_exist']));
+  }
+
+  /**
    * Implements Drupal\Core\Cache\CacheBackendInterface::get().
    */
   function get($cid) {
@@ -53,7 +61,8 @@ class DatabaseBackend implements CacheBackendInterface {
    * Implements Drupal\Core\Cache\CacheBackendInterface::getMultiple().
    */
   function getMultiple(&$cids) {
-    try {
+    $cache = array();
+    if ($this->isDatabaseAvailable()) {
       // When serving cached pages, the overhead of using ::select() was found
       // to add around 30% overhead to the request. Since $this->bin is a
       // variable, this means the call to ::query() here uses a concatenated
@@ -62,7 +71,6 @@ class DatabaseBackend implements CacheBackendInterface {
       // otherwise. When serving an uncached page, the overhead of using
       // ::select() is a much smaller proportion of the request.
       $result = Database::getConnection()->query('SELECT cid, data, created, expire, serialized, tags, checksum FROM {' . Database::getConnection()->escapeTable($this->bin) . '} WHERE cid IN (:cids)', array(':cids' => $cids));
-      $cache = array();
       foreach ($result as $item) {
         $item = $this->prepareItem($item);
         if ($item) {
@@ -70,13 +78,8 @@ class DatabaseBackend implements CacheBackendInterface {
         }
       }
       $cids = array_diff($cids, array_keys($cache));
-      return $cache;
-    }
-    catch (Exception $e) {
-      // If the database is never going to be available, cache requests should
-      // return FALSE in order to allow exception handling to occur.
-      return array();
     }
+    return $cache;
   }
 
   /**
@@ -135,63 +138,70 @@ class DatabaseBackend implements CacheBackendInterface {
       $fields['serialized'] = 0;
     }
 
-    try {
+    if ($this->isDatabaseAvailable()) {
       Database::getConnection()->merge($this->bin)
         ->key(array('cid' => $cid))
         ->fields($fields)
         ->execute();
     }
-    catch (Exception $e) {
-      // The database may not be available, so we'll ignore cache_set requests.
-    }
   }
 
   /**
    * Implements Drupal\Core\Cache\CacheBackendInterface::delete().
    */
   function delete($cid) {
-    Database::getConnection()->delete($this->bin)
-      ->condition('cid', $cid)
-      ->execute();
+    if ($this->isDatabaseAvailable()) {
+      Database::getConnection()->delete($this->bin)
+        ->condition('cid', $cid)
+        ->execute();
+    }
   }
 
   /**
    * Implements Drupal\Core\Cache\CacheBackendInterface::deleteMultiple().
    */
   function deleteMultiple(array $cids) {
-    // Delete in chunks when a large array is passed.
-    do {
-      Database::getConnection()->delete($this->bin)
-        ->condition('cid', array_splice($cids, 0, 1000), 'IN')
-        ->execute();
+    if ($this->isDatabaseAvailable()) {
+      // Delete in chunks when a large array is passed.
+      do {
+        Database::getConnection()->delete($this->bin)
+          ->condition('cid', array_splice($cids, 0, 1000), 'IN')
+          ->execute();
+      }
+      while (count($cids));
     }
-    while (count($cids));
   }
 
   /**
    * Implements Drupal\Core\Cache\CacheBackendInterface::deletePrefix().
    */
   function deletePrefix($prefix) {
-    Database::getConnection()->delete($this->bin)
-      ->condition('cid', Database::getConnection()->escapeLike($prefix) . '%', 'LIKE')
-      ->execute();
+    if ($this->isDatabaseAvailable()) {
+      Database::getConnection()->delete($this->bin)
+        ->condition('cid', Database::getConnection()->escapeLike($prefix) . '%', 'LIKE')
+        ->execute();
+    }
   }
 
   /**
    * Implements Drupal\Core\Cache\CacheBackendInterface::flush().
    */
   function flush() {
-    Database::getConnection()->truncate($this->bin)->execute();
+    if ($this->isDatabaseAvailable()) {
+      Database::getConnection()->truncate($this->bin)->execute();
+    }
   }
 
   /**
    * Implements Drupal\Core\Cache\CacheBackendInterface::expire().
    */
   function expire() {
-    Database::getConnection()->delete($this->bin)
-      ->condition('expire', CacheBackendInterface::CACHE_PERMANENT, '<>')
-      ->condition('expire', REQUEST_TIME, '<')
-      ->execute();
+    if ($this->isDatabaseAvailable()) {
+      Database::getConnection()->delete($this->bin)
+        ->condition('expire', CacheBackendInterface::CACHE_PERMANENT, '<>')
+        ->condition('expire', REQUEST_TIME, '<')
+        ->execute();
+    }
   }
 
   /**
@@ -249,13 +259,15 @@ class DatabaseBackend implements CacheBackendInterface {
    * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateTags().
    */
   public function invalidateTags(array $tags) {
-    foreach ($this->flattenTags($tags) as $tag) {
-      unset(self::$tagCache[$tag]);
-      Database::getConnection()->merge('cache_tags')
-        ->key(array('tag' => $tag))
-        ->fields(array('invalidations' => 1))
-        ->expression('invalidations', 'invalidations + 1')
-        ->execute();
+    if ($this->isDatabaseAvailable()) {
+      foreach ($this->flattenTags($tags) as $tag) {
+        unset(self::$tagCache[$tag]);
+        Database::getConnection()->merge('cache_tags')
+          ->key(array('tag' => $tag))
+          ->fields(array('invalidations' => 1))
+          ->expression('invalidations', 'invalidations + 1')
+          ->execute();
+      }
     }
   }
 
@@ -281,15 +293,12 @@ class DatabaseBackend implements CacheBackendInterface {
       }
    }
     if ($query_tags) {
-      try {
+      if ($this->isDatabaseAvailable()) {
         if ($db_tags = Database::getConnection()->query('SELECT tag, invalidations FROM {cache_tags} WHERE tag IN (:tags)', array(':tags' => $query_tags))->fetchAllKeyed()) {
           self::$tagCache = array_merge(self::$tagCache, $db_tags);
           $checksum += array_sum($db_tags);
         }
       }
-      catch (Exception $e) {
-        // The database may not be available, so we'll ignore cache_set requests.
-      }
     }
     return $checksum;
   }
@@ -298,12 +307,14 @@ class DatabaseBackend implements CacheBackendInterface {
    * Implements Drupal\Core\Cache\CacheBackendInterface::isEmpty().
    */
   function isEmpty() {
-    $this->garbageCollection();
-    $query = Database::getConnection()->select($this->bin);
-    $query->addExpression('1');
-    $result = $query->range(0, 1)
-      ->execute()
-      ->fetchField();
-    return empty($result);
+    if ($this->isDatabaseAvailable()) {
+      $this->garbageCollection();
+      $query = Database::getConnection()->select($this->bin);
+      $query->addExpression('1');
+      $result = $query->range(0, 1)
+        ->execute()
+        ->fetchField();
+      return empty($result);
+    }
   }
 }
diff --git a/core/lib/Drupal/Core/Cache/InstallBackend.php b/core/lib/Drupal/Core/Cache/InstallBackend.php
deleted file mode 100644
index b507684..0000000
--- a/core/lib/Drupal/Core/Cache/InstallBackend.php
+++ /dev/null
@@ -1,151 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\Core\Cache\InstallBackend.
- */
-
-namespace Drupal\Core\Cache;
-
-use Exception;
-
-/**
- * Defines a stub cache implementation to be used during installation.
- *
- * The stub implementation is needed when database access is not yet available.
- * Because Drupal's caching system never requires that cached data be present,
- * these stub functions can short-circuit the process and sidestep the need for
- * any persistent storage. Obviously, using this cache implementation during
- * normal operations would have a negative impact on performance.
- *
- * If there is a database cache, this backend will attempt to clear it whenever
- * possible. The reason for doing this is that the database cache can accumulate
- * data during installation due to any full bootstraps that may occur at the
- * same time (for example, Ajax requests triggered by the installer). If we
- * didn't try to clear it whenever one of the delete function are called, the
- * data in the cache would become stale; for example, the installer sometimes
- * calls variable_set(), which updates the {variable} table and then clears the
- * cache to make sure that the next page request picks up the new value.
- * Not actually clearing the cache here therefore leads old variables to be
- * loaded on the first page requests after installation, which can cause
- * subtle bugs, some of which would not be fixed unless the site
- * administrator cleared the cache manually.
- */
-class InstallBackend extends DatabaseBackend {
-
-  /**
-   * Overrides Drupal\Core\Cache\DatabaseBackend::get().
-   */
-  function get($cid) {
-    return FALSE;
-  }
-
-  /**
-   * Overrides Drupal\Core\Cache\DatabaseBackend::getMultiple().
-   */
-  function getMultiple(&$cids) {
-    return array();
-  }
-
-  /**
-   * Overrides Drupal\Core\Cache\DatabaseBackend::set().
-   */
-  function set($cid, $data, $expire = CacheBackendInterface::CACHE_PERMANENT, array $tags = array()) {}
-
-  /**
-   * Overrides Drupal\Core\Cache\DatabaseBackend::delete().
-   */
-  function delete($cid) {
-    try {
-      if (class_exists('Drupal\Core\Database\Database')) {
-        parent::delete($cid);
-      }
-    }
-    catch (Exception $e) {}
-  }
-
-  /**
-   * Overrides Drupal\Core\Cache\DatabaseBackend::deleteMultiple().
-   */
-  function deleteMultiple(array $cids) {
-    try {
-      if (class_exists('Drupal\Core\Database\Database')) {
-        parent::deleteMultiple($cids);
-      }
-    }
-    catch (Exception $e) {}
-  }
-
-  /**
-   * Overrides Drupal\Core\Cache\DatabaseBackend::deletePrefix().
-   */
-  function deletePrefix($prefix) {
-    try {
-      if (class_exists('Drupal\Core\Database\Database')) {
-        parent::deletePrefix($prefix);
-      }
-    }
-    catch (Exception $e) {}
-  }
-
-  /**
-   * Overrides Drupal\Core\Cache\DatabaseBackend::invalidateTags().
-   */
-  function invalidateTags(array $tags) {
-    try {
-      if (class_exists('Drupal\Core\Database\Database')) {
-        parent::invalidateTags($tags);
-      }
-    }
-    catch (Exception $e) {}
-  }
-
-  /**
-   * Overrides Drupal\Core\Cache\DatabaseBackend::flush().
-   */
-  function flush() {
-    try {
-      if (class_exists('Drupal\Core\Database\Database')) {
-        parent::flush();
-      }
-    }
-    catch (Exception $e) {}
-  }
-
-  /**
-   * Overrides Drupal\Core\Cache\DatabaseBackend::expire().
-   */
-  function expire() {
-    try {
-      if (class_exists('Drupal\Core\Database\Database')) {
-        parent::expire();
-      }
-    }
-    catch (Exception $e) {}
-  }
-
-  /**
-   * Overrides Drupal\Core\Cache\DatabaseBackend::garbageCollection().
-   */
-  function garbageCollection() {
-    try {
-      if (class_exists('Drupal\Core\Database\Database')) {
-        parent::garbageCollection();
-      }
-    }
-    catch (Exception $e) {}
-  }
-
-  /**
-   * Overrides Drupal\Core\Cache\DatabaseBackend::isEmpty().
-   */
-  function isEmpty() {
-    try {
-      if (class_exists('Drupal\Core\Database\Database')) {
-        return parent::isEmpty();
-      }
-    }
-    catch (Exception $e) {}
-    return TRUE;
-  }
-}
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php
index 98ed71d..d0f9f65 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/UnitTestBase.php
@@ -36,7 +36,7 @@ abstract class UnitTestBase extends TestBase {
    * method.
    */
   protected function setUp() {
-    global $conf;
+    global $conf, $install_state;
 
     // Create the database prefix for this test.
     $this->prepareDatabasePrefix();
@@ -65,10 +65,20 @@ abstract class UnitTestBase extends TestBase {
     if (!$this->setupDatabasePrefix) {
       return FALSE;
     }
+    $install_state['database_tables_exist'] = FALSE;
 
     // Set user agent to be consistent with WebTestBase.
     $_SERVER['HTTP_USER_AGENT'] = $this->databasePrefix;
 
     $this->setup = TRUE;
   }
+
+  /**
+   * Removes the "no database tables" global flag.
+   */
+  protected function tearDown() {
+    global $install_state;
+    parent::tearDown();
+    unset($install_state['database_tables_exist']);
+  }
 }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Cache/InstallTest.php b/core/modules/system/lib/Drupal/system/Tests/Cache/InstallTest.php
deleted file mode 100644
index a2199c2..0000000
--- a/core/modules/system/lib/Drupal/system/Tests/Cache/InstallTest.php
+++ /dev/null
@@ -1,131 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\system\Tests\Cache\InstallTest.
- */
-
-namespace Drupal\system\Tests\Cache;
-
-use Drupal\Core\Cache\DatabaseBackend;
-use Drupal\Core\Cache\InstallBackend;
-use Exception;
-
-/**
- * Tests the behavior of the cache backend used for installing Drupal.
- */
-class InstallTest extends CacheTestBase {
-
-  /**
-   * Modules to enable.
-   *
-   * @var array
-   */
-  public static $modules = array('cache_test');
-
-  protected $profile = 'testing';
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Cache install test',
-      'description' => 'Confirm that the cache backend used for installing Drupal works correctly.',
-      'group' => 'Cache',
-    );
-  }
-
-  /**
-   * Tests the behavior of the cache backend used for installing Drupal.
-   *
-   * While Drupal is being installed, the cache system must deal with the fact
-   * that the database is not initially available, and, after it is available,
-   * the fact that other requests that take place while Drupal is being
-   * installed (for example, Ajax requests triggered via the installer's user
-   * interface) may cache data in the database, which needs to be cleared when
-   * the installer makes changes that would result in it becoming stale.
-   *
-   * We cannot test this process directly, so instead we test it by switching
-   * between the normal database cache (Drupal\Core\Cache\DatabaseBackend) and
-   * the installer cache (Drupal\Core\Cache\InstallBackend) while setting and
-   * clearing various items in the cache.
-   */
-  function testCacheInstall() {
-    $database_cache = new DatabaseBackend('test');
-    $install_cache = new InstallBackend('test');
-
-    // Store an item in the database cache, and confirm that the installer's
-    // cache backend recognizes that the cache is not empty.
-    $database_cache->set('cache_one', 'One');
-    $this->assertFalse($install_cache->isEmpty());
-    $database_cache->delete('cache_one');
-    $this->assertTrue($install_cache->isEmpty());
-
-    // Store an item in the database cache, then use the installer's cache
-    // backend to delete it. Afterwards, confirm that it is no longer in the
-    // database cache.
-    $database_cache->set('cache_one', 'One');
-    $this->assertEqual($database_cache->get('cache_one')->data, 'One');
-    $install_cache->delete('cache_one');
-    $this->assertFalse($database_cache->get('cache_one'));
-
-    // Store multiple items in the database cache, then use the installer's
-    // cache backend to delete them. Afterwards, confirm that they are no
-    // longer in the database cache.
-    $database_cache->set('cache_one', 'One');
-    $database_cache->set('cache_two', 'Two');
-    $this->assertEqual($database_cache->get('cache_one')->data, 'One');
-    $this->assertEqual($database_cache->get('cache_two')->data, 'Two');
-    $install_cache->deleteMultiple(array('cache_one', 'cache_two'));
-    $this->assertFalse($database_cache->get('cache_one'));
-    $this->assertFalse($database_cache->get('cache_two'));
-
-    // Store multiple items in the database cache, then use the installer's
-    // cache backend to delete them via a wildcard prefix. Afterwards, confirm
-    // that they are no longer in the database cache.
-    $database_cache->set('cache_one', 'One');
-    $database_cache->set('cache_two', 'Two');
-    $this->assertEqual($database_cache->get('cache_one')->data, 'One');
-    $this->assertEqual($database_cache->get('cache_two')->data, 'Two');
-    $install_cache->deletePrefix('cache_');
-    $this->assertFalse($database_cache->get('cache_one'));
-    $this->assertFalse($database_cache->get('cache_two'));
-
-    // Store multiple items in the database cache, then use the installer's
-    // cache backend to flush the cache. Afterwards, confirm that they are no
-    // longer in the database cache.
-    $database_cache->set('cache_one', 'One');
-    $database_cache->set('cache_two', 'Two');
-    $this->assertEqual($database_cache->get('cache_one')->data, 'One');
-    $this->assertEqual($database_cache->get('cache_two')->data, 'Two');
-    $install_cache->flush();
-    $this->assertFalse($database_cache->get('cache_one'));
-    $this->assertFalse($database_cache->get('cache_two'));
-
-    // Invalidate a tag using the installer cache, then check that the
-    // invalidation was recorded correctly in the database.
-    $install_cache->invalidateTags(array('tag'));
-    $invalidations = db_query("SELECT invalidations FROM {cache_tags} WHERE tag = 'tag'")->fetchField();
-    $this->assertEqual($invalidations, 1);
-
-    // For each cache clearing event that we tried above, try it again after
-    // dropping the {cache_test} table. This simulates the early stages of the
-    // installer (when the database cache tables won't be available yet) and
-    // thereby confirms that the installer's cache backend does not produce
-    // errors if the installer ever calls any code early on that tries to clear
-    // items from the cache.
-    db_drop_table('cache_test');
-    try {
-      $install_cache->isEmpty();
-      $install_cache->delete('cache_one');
-      $install_cache->deleteMultiple(array('cache_one', 'cache_two'));
-      $install_cache->deletePrefix('cache_');
-      $install_cache->flush();
-      $install_cache->expire();
-      $install_cache->garbageCollection();
-      $install_cache->invalidateTags(array('tag'));
-      $this->pass("The installer's cache backend can be used even when the cache database tables are unavailable.");
-    }
-    catch (Exception $e) {
-      $this->fail("The installer's cache backend can be used even when the cache database tables are unavailable.");
-    }
-  }
-}
