1

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

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

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

在我们没有太多请求之前,该方案运行良好。一旦我们有太多的请求,我们将面临大量请求在缓存中找不到任何东西并尝试重新填充它的情况。所以它们都将开始执行慢速 SQL 查询,这将导致高负载影响。解决办法是什么?

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

你如何解决类似的问题?

4

1 回答 1

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
     }
}

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

于 2014-03-09T13:59:47.723 回答