diff --git a/memcache.inc b/memcache.inc
index 4e7cd9651982065b9f36408eec6fd213b2811eb3..0de8058a0177b703b6981490cd7210e54bdc9934 100644
--- a/memcache.inc
+++ b/memcache.inc
@@ -11,11 +11,41 @@ class MemCacheDrupal implements DrupalCacheInterface {
     $this->bin = $bin;
   }
   function get($cid) {
+    global $memcached_prefixes, $memcached_counters;
+    if (!isset($memcached_prefixes)) {
+      $memcached_prefixes = array();
+    }
+    if (!isset($memcached_counters)) {
+      $memcached_counters = array();
+    }
     // Determine when the current bin was last flushed.
     $cache_flush = variable_get("cache_flush_$this->bin", 0);
     // Retrieve the item from the cache.
     $cache = dmemcache_get($cid, $this->bin, $this->memcache);
     if (is_object($cache)) {
+      // Load the prefix directory.
+      if (!isset($memcached_prefixes[$this->bin])) {
+        $memcached_prefixes[$this->bin] = dmemcache_get('.prefixes', $this->bin);
+        if ($memcached_prefixes[$this->bin] === FALSE) {
+          $memcached_prefixes[$this->bin] = array();
+        }
+        $memcached_counters[$this->bin] = array();
+      }
+
+      // Check if the item being fetched matches any prefixes.
+      foreach ($memcached_prefixes[$this->bin] as $prefix) {
+        if (substr($cid, 0, strlen($prefix)) == $prefix) {
+          // On a match, check if we already know the current counter value.
+          if (!isset($memcached_counters[$this->bin][$prefix])) {
+            $memcached_counters[$this->bin][$prefix] = dmemcache_get('.prefix.' . $prefix, $this->bin);
+          }
+
+          // If a matching prefix for this item was cleared after storing it, it is invalid.
+          if (!isset($cache->counters[$prefix]) || $cache->counters[$prefix] < $memcached_counters[$this->bin][$prefix]) {
+            return 0;
+          }
+        }
+      }
       $cache_bins = isset($_SESSION['cache_flush']) ? $_SESSION['cache_flush'] : NULL;
       // Items cached before the cache was last flushed are no longer valid.
       $cache_lifetime = variable_get('cache_lifetime', 0);
@@ -46,14 +76,36 @@ class MemCacheDrupal implements DrupalCacheInterface {
   }
 
   function set($cid, $data, $expire = CACHE_PERMANENT, array $headers = NULL) {
+    global $memcached_prefixes, $memcached_counters;
     $created = time();
 
+    if (!isset($memcached_prefixes[$this->bin])) {
+      $memcached_prefixes[$this->bin] = dmemcache_get('.prefixes', $this->bin);
+      if ($memcached_prefixes[$this->bin] === FALSE) {
+        $memcached_prefixes[$this->bin] = array();
+      }
+      $memcached_counters[$this->bin] = array();
+    }
+
+    $counters = array();
+    // Check if the item being stored matches any prefixes.
+    foreach ($memcached_prefixes[$this->bin] as $prefix) {
+      if (substr($cid, 0, strlen($prefix)) == $prefix) {
+        // On a match, check if we already know the current counter value.
+        if (!isset($memcached_counters[$this->bin][$prefix])) {
+          $memcached_counters[$this->bin][$prefix] = dmemcache_get('.prefix.' . $prefix, $this->bin);
+        }
+        $counters[$prefix] = $memcached_counters[$this->bin][$prefix];
+      }
+    }
+
     // Create new cache object.
     $cache = new stdClass;
     $cache->cid = $cid;
     $cache->data = is_object($data) ? clone $data : $data;
     $cache->created = $created;
     $cache->headers = $headers;
+    $cache->counters = $counters;
     if ($expire == CACHE_TEMPORARY) {
       // Convert CACHE_TEMPORARY (-1) into something that will live in memcache
       // until the next flush.
@@ -77,6 +129,8 @@ class MemCacheDrupal implements DrupalCacheInterface {
   }
 
   function clear($cid = NULL, $wildcard = FALSE) {
+    global $memcached_prefixes, $memcached_counters;
+
     // Default behavior for when cache_clear_all() is called without parameters
     // is to clear all of the expirable entries in the block and page caches.
     if (empty($cid) || ($cid == '*' && $wildcard !== TRUE)) {
@@ -110,7 +164,55 @@ class MemCacheDrupal implements DrupalCacheInterface {
         $_SESSION['cache_flush'] = $cache_bins;
       }
       else {
-        dmemcache_flush($this->bin, $this->memcache);
+        if ($cid == '*') {
+          $cid = '';
+        }
+
+        // Get a memcached object for complex operations.
+        $mc = dmemcache_object($this->bin);
+
+        // Load the prefix directory.
+        if (!isset($memcached_prefixes[$this->bin])) {
+          $memcached_prefixes[$this->bin] = dmemcache_get('.prefixes', $this->bin);
+          if ($memcached_prefixes[$this->bin] === FALSE) {
+            $memcached_prefixes[$this->bin] = array();
+          }
+        }
+
+        // Ensure the prefix being cleared is listed, if not, atomically add it.
+        // Adding new prefixes should be rare.
+        if (!in_array($cid, $memcached_prefixes[$this->bin])) {
+          // Acquire a semaphore.
+          $lock_key = dmemcache_key('.prefixes.lock', $this->bin);
+          while (!$mc->add($lock_key, 1, FALSE, 10)) {
+            usleep(1000);
+          }
+
+          // Get a fresh copy of the prefix directory.
+          $memcached_prefixes[$this->bin] = dmemcache_get('.prefixes', $this->bin);
+          if ($memcached_prefixes[$this->bin] === FALSE) {
+            $memcached_prefixes[$this->bin] = array();
+          }
+
+          // Only add the prefix if it's not in the updated directory.
+          if (!in_array($cid, $memcached_prefixes[$this->bin])) {
+            // Add the new prefix.
+            $memcached_prefixes[$this->bin][] = $cid;
+
+            // Store to memcached.
+            dmemcache_set('.prefixes', $memcached_prefixes[$this->bin], 0, $this->bin);
+
+            // Set the clearing counter to zero.
+            dmemcache_set('.prefix.' . $cid, 0, 0, $this->bin);
+          }
+
+          // Release the semaphore.
+          dmemcache_delete('.prefixes.lock', $this->bin);
+        }
+
+        // Increment the prefix clearing counter.
+        $counter_key = dmemcache_key('.prefix.' . $cid, $this->bin);
+        $memcached_counters[$this->bin][$cid] = $mc->increment($counter_key);
       }
     }
     else {