缓存和避免缓存走火

我们有一个非常昂贵的计算,我们想缓存。 所以我们做类似的事情:

my $result = $cache->get( $key );

unless ($result) {
    $result = calculate( $key );
    $cache->set( $key, $result, '10 minutes' );
}

return $result;

现在,在calculate($key)期间,在将结果存储在缓存中之前,还有其他几个请求进来,它们也开始运行calculate($key) ,并且系统性能受到影响,因为许多进程都在计算相同的内容。

想法:让我们在缓存中放置一个正在计算值的标志,所以其他请求只是等待一个计算完成,所以它们都使用它。 就像是:

my $result = $cache->get( $key );

if ($result) {
    while ($result =~ /Wait, d+ is running calculate../) {
        sleep 0.5;
        $result = $cache->get( $key );
    }
} else {
    $cache->set( $key, "Wait, $$ is running calculate()", '10 minutes' );
    $result = calculate( $key );
    $cache->set( $key, $result, '10 minutes' );
}


return $result;

现在,这打开了一个全新的蠕虫罐。 如果$$在设置缓存之前死亡,该怎么办? 如果......如果......所有这些都是可以解决的,但是由于CPAN中没有这样做(CPAN中的所有内容都有),我开始怀疑:

有更好的方法吗? 是否有一个特殊的原因,例如Perl的CacheCache::Cache类不提供这样的机制? 有没有一个我可以使用的尝试和真正的模式?

理想情况是CPAN模块的debian软件包已经在挤压或者尤里卡时刻,在那里我看到了我的方式错误...... :-)

编辑:我从那以后才知道这被称为缓存踩踏事件并更新了问题的标题。


flock()它。

由于您的工作进程都在同一个系统上,因此您可以使用良好的老式文件锁定来序列化昂贵的calculate()离子。 作为奖励,这种技术出现在几个核心文档中。

use Fcntl qw(:DEFAULT :flock);    # warning:  this code not tested

use constant LOCKFILE => 'you/customize/this/please';

my $result = $cache->get( $key );

unless ($result) {
    # Get an exclusive lock
    my $lock;
    sysopen($lock, LOCKFILE, O_WRONLY|O_CREAT) or die;
    flock($lock, LOCK_EX) or die;

    # Did someone update the cache while we were waiting?
    $result = $cache->get( $key );

    unless ($result) {
        $result = calculate( $key );
        $cache->set( $key, $result, '10 minutes' );
    }

    # Exclusive lock released here as $lock goes out of scope
}

return $result;

好处:工人死亡会立即释放$lock

风险:LOCK_EX可以永远阻止,这是一段很长的时间。 避免使用SIGSTOPs,可能会适应alarm()

扩展:如果您不想序列化所有calculate()调用,而只是调用相同的$key或某组键,则您的工作人员可以flock() /some/lockfile.$key_or_a_hash_of_the_key


使用锁定? 或者,也许这将是一个矫枉过正的? 或者如果可能的话,预先离线计算结果然后在线使用它?


虽然它可能(或可能不)对您的用例过度,但您是否考虑过使用消息队列进行处理? RabbitMQ目前似乎是Perl社区的热门选择,它通过AnyEvent :: RabbitMQ模块提供支持。

在这种情况下,基本策略是在需要calculate新密钥时向消息队列提交请求。 然后可以将队列设置为一次只calculate一个键(按请求的顺序),如果这是您可以可靠处理的所有内容。 或者,如果您可以同时安全地计算多个密钥,则队列还可以用于合并对同一个密钥的多个请求,计算一次并将结果返回给请求该密钥的所有客户端。

当然,这会增加一些复杂性,AnyEvent要求的编程风格有些不同于你可能习惯的(我会提供一个例子,但我从来没有真正掌握过它),但它可能会提供在效率和可靠性方面获得足够的收益,以使这些成本值得您享受

链接地址: http://www.djcxy.com/p/10801.html

上一篇: Caching & avoiding Cache Stampedes

下一篇: Finite Field (Galois Field) Linear Algebra Library for C (not C++)