缓存失效导致高负载

时间:2014-03-09 13:45:49

标签: php caching memcached high-load

假设我们的php脚本每秒有10K个请求。

每个请求都在memcached(或任何其他缓存存储)中检查缓存。如果找到缓存 - 一切正常,并返回缓存值。如果找不到缓存,我们正在进行缓慢的SQL查询来填充缓存。这是最常见和最简单的缓存方案:

$result = $this->loadFromCache($key);
if (empty($result)) {
    $result = $this->makeSlowSqlQuery();
    $this->writeToCache($key, $result);
}
//do something with $result;

这个方案很有效,直到我们没有太多请求。一旦我们收到太多请求,当大量请求在缓存中找不到任何内容并尝试重新填充时,我们将面临这种情况。因此,所有这些都将开始执行慢速SQL查询,这将导致高负载影响。解决方案是什么?

作为可能的解决方案,我看到以下情况:首先发现缓存无效的请求应该创建一些触发器,表示缓存重新填充已经启动,另一个请求应该等待新缓存或使用旧版本(以前的版本)。

你如何解决类似问题?

1 个答案:

答案 0 :(得分:1)

你真正想要的是锁定模式:

$lockPrefix = "!lock__";
$result = $this->loadFromCache($key);
if (empty($result)) {
     $sleepLimit = 2000; // 2s timeout
     $sleepCount = 0;
     $cacheBlocked = 0;
     while ($this->loadFromCache($lockPrefix . $key) == 1) {
         // signal that something else is updating the cache
         $cacheBlocked = 1;
         // sleep for 1ms
         usleep(1000);
         // timeout logic...
         $sleepCount++
         if ($sleepCount == $sleepLimit) {
             die("Cache read timeout.");
         }
     }
     if ($cacheBlocked == 1) {
         // something else updated the cache while we were waiting
         // so we can just read that result now
         $result = $this->loadFromCache($key);
     } else {
         $this->writeToCache($lockPrefix . $key, 1); // lock
         $result = $this->makeSlowSqlQuery();
         $this->writeToCache($key, $result);
         $this->writeToCache($lockPrefix . $key, 0); // release
     }
}

这个想法是缓存是全局的,因此可以用来保存跨请求的锁模式。您实际上是在缓存条目中创建一个互斥锁,并添加了一些逻辑,以确保只启动一个慢速查询。