diff --git a/includes/cache-install.inc b/includes/cache-install.inc
deleted file mode 100644
index 8bcf8b7..0000000
--- a/includes/cache-install.inc
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-
-/**
- * @file
- * Provides a stub cache implementation to be used during installation.
- */
-
-/**
- * A stub cache implementation to be used during the installation process.
- *
- * 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.
- */
-class DrupalFakeCache extends DrupalDatabaseCache implements DrupalCacheInterface {
-  function get($cid) {
-    return FALSE;
-  }
-
-  function getMultiple(&$cids) {
-    return array();
-  }
-
-  function set($cid, $data, $expire = CACHE_PERMANENT) {
-  }
-
-  function deletePrefix($cid) {
-    try {
-      if (class_exists('Database')) {
-        parent::deletePrefix($cid);
-      }
-    }
-    catch (Exception $e) {
-    }
-  }
-
-  function clear($cid = NULL, $wildcard = FALSE) {
-    // If there is a database cache, 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 this function is 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.
-    try {
-      if (class_exists('Database')) {
-        parent::clear($cid, $wildcard);
-      }
-    }
-    // If the attempt at clearing the cache causes an error, that means that
-    // either the database connection is not set up yet or the relevant cache
-    // table in the database has not yet been created, so we can safely do
-    // nothing here.
-    catch (Exception $e) {
-    }
-  }
-
-  function isEmpty() {
-    return TRUE;
-  }
-}
diff --git a/includes/cache.inc b/includes/cache.inc
index fcf3e5e..5518a6d 100644
--- a/includes/cache.inc
+++ b/includes/cache.inc
@@ -16,13 +16,12 @@
  *   The cache object associated with the specified bin.
  */
 function cache($bin = 'cache') {
+  $cache_objects = &drupal_static(__FUNCTION__, array());
+
   // Temporary backwards compatibiltiy layer, allow old style prefixed cache
   // bin names to be passed as arguments.
   $bin = str_replace('cache_', '', $bin);
 
-  // We do not use drupal_static() here because we do not want to change the
-  // storage of a cache bin mid-request.
-  static $cache_objects;
   if (!isset($cache_objects[$bin])) {
     $class = variable_get('cache_class_' . $bin);
     if (!isset($class)) {
@@ -30,6 +29,7 @@ function cache($bin = 'cache') {
     }
     $cache_objects[$bin] = new $class($bin);
   }
+
   return $cache_objects[$bin];
 }
 
@@ -182,6 +182,7 @@ function cache_clear_all($cid = NULL, $bin = NULL, $wildcard = FALSE) {
  *
  * @param $bin
  *   The cache bin to check.
+ *
  * @return
  *   TRUE if the cache bin specified is empty.
  */
@@ -190,6 +191,99 @@ function cache_is_empty($bin) {
 }
 
 /**
+ * Check if a cache bin is available.
+ *
+ * A cache bin is considered available if it is currently able to be written to
+ * and read from.
+ *
+ * @param $bin
+ *   The cache bin to check.
+ *
+ * @return
+ *   TRUE if the cache bin specified is available.
+ */
+function cache_is_available($bin) {
+  return cache($bin)->isAvailable();
+}
+
+/**
+ * Temporarily override a cache implementation for the current page request.
+ *
+ * This function should be used rarely - for example, in heavily dynamic
+ * environments like the Drupal installer that need to use multiple caches
+ * during the same page request. To permanently change a site's cache
+ * implementations (taking effect on the following page request), use
+ * variable_set() or variable_del() instead.
+ *
+ * @param $class
+ *   The name of the class whose cache implementation will be used. For
+ *   example, if this is set to 'DrupalNullCache', caching will be turned off
+ *   for the remainder of the current page request.
+ * @param $bin
+ *   (optional) The cache bin which will use the new class. If not provided,
+ *   the new class will be used as the default for all cache bins that do not
+ *   have their own bin-specific implementations.
+ *
+ * @see cache_implementation_reset()
+ * @see DrupalCacheInterface
+ */
+function cache_implementation_override($class, $bin = NULL) {
+  _cache_implementation_override($class, $bin);
+}
+
+/**
+ * Temporarily reset a cache implementation to its default value for the current page request.
+ *
+ * This function should be used rarely - for example, in heavily dynamic
+ * environments like the Drupal installer that need to use multiple caches
+ * during the same page request. To permanently change a site's cache
+ * implementations (taking effect on the following page request), use
+ * variable_set() or variable_del() instead.
+ *
+ * @param $bin
+ *   (optional) The cache bin which will be reset to its default
+ *   implementation. If not provided, the default cache implementation (i.e.,
+ *   the one used by all cache bins that do not have their own bin-specific
+ *   implementations) will be reset to use the default 'DrupalDatabaseCache'.
+ *
+ * @see cache_implementation_override()
+ * @see DrupalCacheInterface
+ */
+function cache_implementation_reset($bin = NULL) {
+  _cache_implementation_override(NULL, $bin);
+}
+
+/**
+ * Private helper function for temporarily overriding cache implementations.
+ *
+ * @param $class
+ *   (optional) The name of the class whose cache implementation will be used,
+ *   or NULL to reset to the default cache implementation.
+ * @param $bin
+ *   (optional) The cache bin to override or reset, or NULL to override or
+ *   reset the default cache implementation.
+ *
+ * @see cache_implementation_override()
+ * @see cache_implementation_reset()
+ */
+function _cache_implementation_override($class = NULL, $bin = NULL) {
+  global $conf;
+
+  $name = isset($bin) ? 'cache_class_' . $bin : 'cache_default_class';
+
+  if (isset($class)) {
+    $conf[$name] = $class;
+  }
+  else {
+    unset($conf[$name]);
+  }
+
+  // Ensure that the new cache implementation is used for the remainder of the
+  // current page request.
+  drupal_static_reset('cache');
+}
+
+/**
  * Interface for cache implementations.
  *
  * All cache implementations have to implement this interface.
@@ -345,6 +439,17 @@ interface DrupalCacheInterface {
    *   TRUE if the cache bin specified is empty.
    */
   function isEmpty();
+
+  /**
+   * Check if a cache bin is available.
+   *
+   * A cache bin is considered available if it is currently able to be written
+   * to and read from.
+   *
+   * @return
+   *   TRUE if the cache bin specified is available.
+   */
+  function isAvailable();
 }
 
 /**
@@ -389,6 +494,10 @@ class DrupalNullCache implements DrupalCacheInterface {
   function isEmpty() {
     return TRUE;
   }
+
+  function isAvailable() {
+    return TRUE;
+  }
 }
 
 /**
@@ -615,4 +724,8 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
       ->fetchField();
     return empty($result);
   }
+
+  function isAvailable() {
+    return function_exists('db_table_exists') && db_table_exists($this->bin);
+  }
 }
diff --git a/includes/install.core.inc b/includes/install.core.inc
index 7405f9f..2ff44d3 100644
--- a/includes/install.core.inc
+++ b/includes/install.core.inc
@@ -237,9 +237,6 @@ function install_begin_request(&$install_state) {
 
   drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
 
-  // This must go after drupal_bootstrap(), which unsets globals!
-  global $conf;
-
   require_once DRUPAL_ROOT . '/modules/system/system.install';
   require_once DRUPAL_ROOT . '/includes/common.inc';
   require_once DRUPAL_ROOT . '/includes/file.inc';
@@ -262,18 +259,10 @@ function install_begin_request(&$install_state) {
   drupal_load('module', 'entity');
   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.
+  // Initialize the cache implementation that will be used for the current
+  // stage of the installer.
   require_once DRUPAL_ROOT . '/includes/cache.inc';
-  require_once DRUPAL_ROOT . '/includes/cache-install.inc';
-  $conf['cache_default_class'] = 'DrupalFakeCache';
+  install_set_cache_implementation();
 
   // Prepare for themed output. We need to run this at the beginning of the
   // page request to avoid a different theme accidentally getting set. (We also
@@ -292,6 +281,15 @@ function install_begin_request(&$install_state) {
 
     // Verify the last completed task in the database, if there is one.
     $task = install_verify_completed_task();
+
+    // If we connected to the database, we might be able to use the default
+    // Drupal database cache implementation for the remainder of the page
+    // request. It is important to check this (and switch if possible)
+    // immediately to avoid cache inconsistencies; see comments in
+    // install_set_cache_implementation() for more details.
+    if (!empty($task)) {
+      install_set_cache_implementation();
+    }
   }
   else {
     $task = NULL;
@@ -764,6 +762,13 @@ function install_system_module(&$install_state) {
   // Install system.module.
   drupal_install_system();
 
+  // Since the system module is now installed, we should be able to use the
+  // default Drupal database cache implementation for the remainder of the page
+  // request. It is important to check this (and switch if possible)
+  // immediately to avoid cache inconsistencies; see comments in
+  // install_set_cache_implementation() for more details.
+  install_set_cache_implementation();
+
   // Enable the user module so that sessions can be recorded during the
   // upcoming bootstrap step.
   module_enable(array('user'), FALSE);
@@ -781,6 +786,36 @@ function install_system_module(&$install_state) {
 }
 
 /**
+ * Appropriately sets the cache implementation used during installation.
+ *
+ * The installer is unusual in that when it interacts with Drupal's cache
+ * system, it does not always use the same cache implementation throughout the
+ * entire page request. For example, the early stages of the installer must use
+ * the (stub) DrupalNullCache cache implementation, because the database is not
+ * set up yet so cached data cannot be read or written there. After the
+ * database is available, though, it is important to switch to the default
+ * cache implementation immediately. This is because any full bootstraps that
+ * occur via index.php while Drupal is being installed (for example, Ajax
+ * requests triggered by the installer) or shortly after it has been installed
+ * (the first normal page requests after installation is complete) will
+ * automatically use the default cache. If the two systems did not use the same
+ * cache, changes made by the installer would not clear the cached data seen by
+ * the index.php requests, and those index.php requests would therefore be at
+ * risk of working with stale data.
+ *
+ * When called by the installer, this function ensures that the correct cache
+ * implementation is currently being used, to avoid any of the above problems.
+ */
+function install_set_cache_implementation() {
+  // Attempt to reset the cache implementation to its default. If the default
+  // is not available, fall back on the stub implementation.
+  cache_implementation_reset();
+  if (!cache_is_available('cache')) {
+    cache_implementation_override('DrupalNullCache');
+  }
+}
+
+/**
  * Verify and return the last installation task that was completed.
  *
  * @return
@@ -1514,9 +1549,10 @@ function install_finished(&$install_state) {
   $output = '<p>' . st('Congratulations, you installed @drupal!', array('@drupal' => drupal_install_profile_distribution_name())) . '</p>';
   $output .= '<p>' . (isset($messages['error']) ? st('Review the messages above before visiting <a href="@url">your new site</a>.', array('@url' => url(''))) : st('<a href="@url">Visit your new site</a>.', array('@url' => url('')))) . '</p>';
 
-  // Flush all caches to ensure that any full bootstraps during the installer
-  // do not leave stale cached data, and that any content types or other items
-  // registered by the install profile are registered correctly.
+  // Flush all caches to ensure that any content types or other items created
+  // by the install profile are registered correctly, and to clear out any
+  // incorrect data that may have been cached when Drupal was not fully set up
+  // yet.
   drupal_flush_all_caches();
 
   // Remember the profile which was used.
diff --git a/modules/simpletest/simpletest.info b/modules/simpletest/simpletest.info
index ba817da..d258f90 100644
--- a/modules/simpletest/simpletest.info
+++ b/modules/simpletest/simpletest.info
@@ -23,6 +23,7 @@ files[] = tests/filetransfer.test
 files[] = tests/form.test
 files[] = tests/graph.test
 files[] = tests/image.test
+files[] = tests/install.test
 files[] = tests/lock.test
 files[] = tests/mail.test
 files[] = tests/menu.test
diff --git a/modules/simpletest/tests/install.test b/modules/simpletest/tests/install.test
new file mode 100644
index 0000000..174a472
--- /dev/null
+++ b/modules/simpletest/tests/install.test
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * @file
+ * Tests for the install system.
+ */
+
+/**
+ * Tests that caching works correctly while Drupal is being installed.
+ */
+class InstallCacheTestCase extends DrupalWebTestCase {
+  protected $profile = 'testing';
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Installer cache functionality',
+      'description' => 'Test that caching behaves correctly during installation.',
+      'group' => 'Installer',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('install_test');
+    require_once DRUPAL_ROOT . '/includes/install.core.inc';
+  }
+
+  /**
+   * Test that caches do not become out-of-date while Drupal is being installed.
+   *
+   * Although we cannot test the installer directly, we can still simulate its
+   * behavior here to check that there are no cache inconsistency problems.
+   */
+  function testInstallerCache() {
+    // Start by making a request to a regular Drupal callback. This simulates,
+    // for example, an Ajax request made by the installer, which hits
+    // index.php. It will result in various database caches (for example, the
+    // variable cache) getting populated.
+    $this->drupalGet('install_test');
+
+    // Now simulate a page request inside the installer (using the installer's
+    // cache implementation) which changes a variable.
+    install_set_cache_implementation();
+    $site_name = 'Test site name';
+    variable_set('site_name', $site_name);
+
+    // Finally, simulate another Ajax request, and ensure that it sees the
+    // updated value of the variable.
+    $this->drupalGet('install_test');
+    $this->assertText(t('The current site name is @site_name', array('@site_name' => $site_name)));
+  }
+}
diff --git a/modules/simpletest/tests/install_test.info b/modules/simpletest/tests/install_test.info
new file mode 100644
index 0000000..5176ea2
--- /dev/null
+++ b/modules/simpletest/tests/install_test.info
@@ -0,0 +1,6 @@
+name = "Install test"
+description = "Support module for testing the Drupal installer."
+package = Testing
+version = VERSION
+core = 8.x
+hidden = TRUE
diff --git a/modules/simpletest/tests/install_test.module b/modules/simpletest/tests/install_test.module
new file mode 100644
index 0000000..d55d8f6
--- /dev/null
+++ b/modules/simpletest/tests/install_test.module
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Support module for testing the Drupal installer.
+ */
+
+/**
+ * Implements hook_menu().
+ */
+function install_test_menu() {
+  $items['install_test'] = array(
+    'title' => 'Install test callback',
+    'page callback' => 'install_test_callback',
+    'type' => MENU_CALLBACK,
+    'access callback' => TRUE,
+  );
+
+  return $items;
+}
+
+/**
+ * Menu callback to test the value of variables during a Drupal installation.
+ */
+function install_test_callback() {
+  $site_name = variable_get('site_name', 'Drupal');
+  return t('The current site name is @site_name', array('@site_name' => $site_name));
+}
