假设我们的php脚本每秒有10K个请求。
每个请求都在memcached(或任何其他缓存存储)中检查缓存。如果找到缓存 - 一切正常,并返回缓存值。如果找不到缓存,我们正在进行缓慢的SQL查询来填充缓存。这是最常见和最简单的缓存方案:
$result = $this->loadFromCache($key);
if (empty($result)) {
$result = $this->makeSlowSqlQuery();
$this->writeToCache($key, $result);
}
//do something with $result;
这个方案很有效,直到我们没有太多请求。一旦我们收到太多请求,当大量请求在缓存中找不到任何内容并尝试重新填充时,我们将面临这种情况。因此,所有这些都将开始执行慢速SQL查询,这将导致高负载影响。解决方案是什么?
作为可能的解决方案,我看到以下情况:首先发现缓存无效的请求应该创建一些触发器,表示缓存重新填充已经启动,另一个请求应该等待新缓存或使用旧版本(以前的版本)。
你如何解决类似问题?
答案 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
}
}
这个想法是缓存是全局的,因此可以用来保存跨请求的锁模式。您实际上是在缓存条目中创建一个互斥锁,并添加了一些逻辑,以确保只启动一个慢速查询。