Google Appengine(GAE)::如何在使用GAE memcached时防止Dogpile效应?

时间:2013-01-13 06:35:11

标签: google-app-engine memcached google-cloud-datastore

我需要缓存一些代价高昂的查询(5秒)。我最近了解了使用memcache时的dogpile影响。一种解决方案是使用缓存键值对上的锁来防止这种影响。由于GAE memcached不支持对memcached键值的锁定,防止dogpile效果的最佳做法是什么?

3 个答案:

答案 0 :(得分:3)

这取决于你的应用程序在做什么,但这里有三种方法可能很有用(没有一种方法适用于所有方案,它们都有缺点):

如果您可以使用“稍等一会儿再试一次”来回复请求,那么在您花费时间重新处理实际值之前,设置一个表示未完成的内存缓存的标记可能会有所帮助(会有所帮助)仍然是get和第一组之间竞争的可能性,但它比等待查询完成要短得多):

value = memcache.get(key)
if value is None:
  memcache.set(key, 'recalculating')
  // do the slow thing
  memcache.set(key, actual_result)

return value

或者,如果您在缓存结果上设置了生命周期,但是可以处理接收稍微陈旧数据的客户端,然后使用超时和密钥缓存您的值,则缓存没有超时的副本和不同的密钥,然后错过使用此副本重新填充缓存,同时重新处理一个新的值(再次,仍然是获取/设置之间的竞争的机会,并且也可能驱逐该副本。):

value = memcache.get(key)
if value is None:
  memcache.set(key, cache.get(key+'copy'))
  // do the slow thing
  memcache.set(key, actual_result, 30)
  memcache.set(key+'copy', actual_result)

return value

第三个更简单,只需让后端不断进行查询并让它更新缓存,这样前端请求就不太可能必须这样做。但这确实意味着,无论任何人使用什么值,查询都会完成。

答案 1 :(得分:1)

忽略它。

喝Google Kool-Aid并相信您不会因同时查询到数据存储区而遭受重大数据库冲击。无论如何,堆垛效应是暂时的,基础设施应该吸收它。您只需支付更多查询费用。

除非你真的有足够的流量并且花费了大量的金钱,否则你的优先事项可能差不多。

答案 2 :(得分:0)

使用信号量锁可以防止Dogpile效应。如果值已过期,则第一个进程获取锁定并开始生成新值。所有后续请求都会检查是否已获取锁定并提供过时内容(如果是)。生成新值后,将释放锁定。

缓存值应该延长使用寿命,因此它们在过期时不会被物理删除,如果有需要,它们仍然可以提供。

以下是它在PHP中的工作原理(应该很容易在Python中复制)。

从缓存存储中获取缓存值。

$value = $this->store->get($key);

$ value是一个值对象。

检查缓存值是否已过期。如果没有过期,请提供服务。

if ($value && !$value->isStale()) {
    return $value->getResult();
}

否则,获取锁定,因此只有一个进程重新生成新值。

$lock_acquired = $this->acquireLock($key, $grace_ttl);

如果无法获取锁定,则表示已经有其他进程重新生成它,所以让我们只提供当前(陈旧)值。

if (!$lock_acquired) {
    return $value->getResult();
}

否则(已获取锁定),重新生成新值。

$result = ...

在缓存存储中保存重新生成的值。添加宽限期,如果其他进程需要,可以提供过时的结果。

$expiration_timestamp = time() + $ttl;
$value = new Value($result, $expiration_timestamp);

$real_ttl = $ttl + $grace_ttl;
$this->store->set($key, $value, $real_ttl);

释放锁定。

$this->releaseLock($key);

完整的PHP实施:https://github.com/sobstel/metaphore/blob/master/src/Cache.php。您也可以尝试使用MintCache:https://djangosnippets.org/snippets/155/