diff --git a/includes/cache.inc b/includes/cache.inc
index 4c2bed3..76892a3 100644
--- a/includes/cache.inc
+++ b/includes/cache.inc
@@ -336,9 +336,6 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
    */
   function getMultiple(&$cids) {
     try {
-      // Garbage collection necessary when enforcing a minimum cache lifetime.
-      $this->garbageCollection($this->bin);
-
       // When serving cached pages, the overhead of using db_select() was found
       // to add around 30% overhead to the request. Since $this->bin is a
       // variable, this means the call to db_query() here uses a concatenated
@@ -373,37 +370,22 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
   protected function garbageCollection() {
     $cache_lifetime = variable_get('cache_lifetime', 0);
 
-    // Clean-up the per-user cache expiration session data, so that the session
-    // handler can properly clean-up the session data for anonymous users.
-    if (isset($_SESSION['cache_expiration'])) {
-      $expire = REQUEST_TIME - $cache_lifetime;
-      foreach ($_SESSION['cache_expiration'] as $bin => $timestamp) {
-        if ($timestamp < $expire) {
-          unset($_SESSION['cache_expiration'][$bin]);
-        }
-      }
-      if (!$_SESSION['cache_expiration']) {
-        unset($_SESSION['cache_expiration']);
-      }
-    }
-
-    // Garbage collection of temporary items is only necessary when enforcing
-    // a minimum cache lifetime.
-    if (!$cache_lifetime) {
-      return;
-    }
-    // When cache lifetime is in force, avoid running garbage collection too
-    // often since this will remove temporary cache items indiscriminately.
-    $cache_flush = variable_get('cache_flush_' . $this->bin, 0);
-    if ($cache_flush && ($cache_flush + $cache_lifetime <= REQUEST_TIME)) {
-      // Reset the variable immediately to prevent a meltdown in heavy load situations.
-      variable_set('cache_flush_' . $this->bin, 0);
-      // Time to flush old cache data
-      db_delete($this->bin)
-        ->condition('expire', CACHE_PERMANENT, '<>')
-        ->condition('expire', $cache_flush, '<=')
-        ->execute();
-    }
+    // Clear expired items from the cache.
+    db_delete($this->bin)
+      ->condition('expire', CACHE_PERMANENT, '>')
+      ->condition('expire', REQUEST_TIME, '<')
+      ->execute();
+
+    // Clear old temporary cache items. 'Old' is defined as older than the
+    // minimum cache lifetime, or 24 hours, whichever is the greatest.
+    $old = max($cache_lifetime, 86400);
+
+    // Clear CACHE_TEMPORARY items that are older than the minimum cache
+    // lifetime.
+    db_delete($this->bin)
+      ->condition('expire', CACHE_TEMPORARY)
+      ->condition('created', REQUEST_TIME - $old, '<')
+      ->execute();
   }
 
   /**
@@ -420,8 +402,6 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
    *   valid item to load.
    */
   protected function prepareItem($cache) {
-    global $user;
-
     if (!isset($cache->data)) {
       return FALSE;
     }
@@ -443,9 +423,30 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
   }
 
   /**
+   * Cleans up the per-session cache expiration data.
+   */
+  protected function cleanSession() {
+    $cache_lifetime = variable_get('cache_lifetime', 0);
+    if (isset($_SESSION['cache_expiration'])) {
+      $expire = REQUEST_TIME - $cache_lifetime;
+      foreach ($_SESSION['cache_expiration'] as $bin => $timestamp) {
+        if ($timestamp < $expire) {
+          unset($_SESSION['cache_expiration'][$bin]);
+        }
+      }
+      if (!$_SESSION['cache_expiration']) {
+        unset($_SESSION['cache_expiration']);
+      }
+    }
+  }
+
+  /**
    * Implements DrupalCacheInterface::set().
    */
   function set($cid, $data, $expire = CACHE_PERMANENT) {
+    // When setting cache items, clean up old session data in case it is stale.
+    $this->cleanSession();
+
     $fields = array(
       'serialized' => 0,
       'created' => REQUEST_TIME,
@@ -475,32 +476,24 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
    * Implements DrupalCacheInterface::clear().
    */
   function clear($cid = NULL, $wildcard = FALSE) {
-    global $user;
-
     if (empty($cid)) {
-      if (variable_get('cache_lifetime', 0)) {
-        // We store the time in the current user's session. We then simulate
-        // that the cache was flushed for this user by not returning cached
-        // data that was cached before the timestamp.
-        $_SESSION['cache_expiration'][$this->bin] = REQUEST_TIME;
-
-        $cache_flush = variable_get('cache_flush_' . $this->bin, 0);
-        if ($cache_flush == 0) {
-          // This is the first request to clear the cache, start a timer.
-          variable_set('cache_flush_' . $this->bin, REQUEST_TIME);
-        }
-        elseif (REQUEST_TIME > ($cache_flush + variable_get('cache_lifetime', 0))) {
-          // Clear the cache for everyone, cache_lifetime seconds have
-          // passed since the first request to clear the cache.
-          db_delete($this->bin)
-            ->condition('expire', CACHE_PERMANENT, '<>')
-            ->condition('expire', REQUEST_TIME, '<')
-            ->execute();
-          variable_set('cache_flush_' . $this->bin, 0);
-        }
+      if ($cache_lifetime = variable_get('cache_lifetime', 0)) {
+        // Clear expired items from the cache.
+        db_delete($this->bin)
+          ->condition('expire', CACHE_PERMANENT, '>')
+          ->condition('expire', REQUEST_TIME, '<')
+          ->execute();
+
+        // Clear CACHE_TEMPORARY items that are older than the minimum cache
+        // lifetime.
+        db_delete($this->bin)
+          ->condition('expire', CACHE_TEMPORARY)
+          ->condition('created', REQUEST_TIME - $cache_lifetime, '<')
+          ->execute();
       }
       else {
-        // No minimum cache lifetime, flush all temporary cache entries now.
+        // No minimum cache lifetime. Flush all expired and temporary cache
+        // entries now.
         db_delete($this->bin)
           ->condition('expire', CACHE_PERMANENT, '<>')
           ->condition('expire', REQUEST_TIME, '<')
diff --git a/modules/simpletest/tests/cache.test b/modules/simpletest/tests/cache.test
index b42de36..a1a4934 100644
--- a/modules/simpletest/tests/cache.test
+++ b/modules/simpletest/tests/cache.test
@@ -320,6 +320,44 @@ class CacheClearCase extends CacheTestCase {
   }
 
   /**
+   * Tests CACHE_TEMPORARY behavior.
+   */
+  function testCacheTemporary() {
+    $cache = _cache_get_object($this->default_bin);
+    // Set a permanent and temporary cache item.
+    $cache->set('test_cid_clear1', $this->default_value, CACHE_TEMPORARY);
+    $cache->set('test_cid_clear2', $this->default_value);
+    // Also set expired and yet to expire cache items.
+    $cache->set('test_cid_clear3', $this->default_value, REQUEST_TIME - 1000);
+    $cache->set('test_cid_clear4', $this->default_value, REQUEST_TIME + 1000);
+    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value));
+    $this->assertTrue($this->checkCacheExists('test_cid_clear2', $this->default_value));
+    $this->assertTrue($this->checkCacheExists('test_cid_clear3', $this->default_value));
+    $this->assertTrue($this->checkCacheExists('test_cid_clear4', $this->default_value));
+
+    // Clear all items in the bin. Only the temporary and expired items should
+    // be removed.
+    $cache->clear();
+    $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value));
+    $this->assertTrue($this->checkCacheExists('test_cid_clear2', $this->default_value));
+    $this->assertFalse($this->checkCacheExists('test_cid_clear3', $this->default_value));
+    $this->assertTrue($this->checkCacheExists('test_cid_clear4', $this->default_value));
+
+    // Set a minimum cache lifetime.
+    $cache->set('test_cid_clear1', $this->default_value, CACHE_TEMPORARY);
+    $cache->set('test_cid_clear3', $this->default_value, REQUEST_TIME - 1000);
+    $cache->set('test_cid_clear4', $this->default_value, REQUEST_TIME + 1000);
+    $this->setUpLifetime(300);
+
+    // Now after clearing the bin, only the expired item should be removed.
+    $cache->clear();
+    $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value));
+    $this->assertTrue($this->checkCacheExists('test_cid_clear2', $this->default_value));
+    $this->assertFalse($this->checkCacheExists('test_cid_clear3', $this->default_value));
+    $this->assertTrue($this->checkCacheExists('test_cid_clear4', $this->default_value));
+  }
+
+  /**
    * Test drupal_flush_all_caches().
    */
   function testFlushAllCaches() {
diff --git a/modules/system/system.install b/modules/system/system.install
index 323b7b3..cfdfe62 100644
--- a/modules/system/system.install
+++ b/modules/system/system.install
@@ -712,7 +712,7 @@ function system_schema() {
       ),
     ),
     'indexes' => array(
-      'expire' => array('expire'),
+      'expire_created' => array('expire', 'created'),
     ),
     'primary key' => array('cid'),
   );
@@ -3193,6 +3193,23 @@ function system_update_7080() {
 }
 
 /**
+ * Update all cache tables to use expire and created for their indexes.
+ */
+function system_update_7081() {
+  // Find all potential cache tables.
+  $tables = db_find_tables(Database::getConnection()->prefixTables('{cache}') . '%');
+
+  foreach ($tables as $table) {
+    // Assume a valid cache table if both 'cid' and 'data' columns exist.
+    if (db_field_exists($table, 'cid') && db_field_exists($table, 'data')) {
+      if (db_drop_index($table, 'expire')) {
+        db_add_index($table, 'expire_created', array('expire', 'created'));
+      }
+    }
+  }
+}
+
+/**
  * @} End of "defgroup updates-7.x-extra".
  * The next series of updates should start at 8000.
  */
diff --git a/modules/system/system.module b/modules/system/system.module
index 8a080fa..98071a4 100644
--- a/modules/system/system.module
+++ b/modules/system/system.module
@@ -3071,7 +3071,15 @@ function system_cron() {
     $cache_tables = $cache_object->data;
   }
   foreach ($cache_tables as $table) {
-    cache_clear_all(NULL, $table);
+    // Expire all temporary and expired cache items. Limit this to once every 24
+    // hours by default to avoid wiping page and block caches every cron run.
+    $cache = _cache_get_object($table);
+    $name = 'cache_garbage_collect_' . $table;
+    $window = max(variable_get('cache_lifetime', 0), variable_get('cache_garbage_collection_frequency', 86400));
+    if (flood_is_allowed($name, 1, $window, 'cron')) {
+      flood_register_event($name, $window, 'cron');
+      $cache->clear();
+    }
   }
 
   // Cleanup the batch table and the queue for failed batches.
