缓存和避免缓存加盖 - 多个同时计算

Pet*_*rch 7 perl caching

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

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

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

return $result;
Run Code Online (Sandbox Code Playgroud)

现在,在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;
Run Code Online (Sandbox Code Playgroud)

现在,它开辟了一整套新的蠕虫.如果$$在设置缓存之前死亡,该怎么办?如果,要是......他们都可以解决的,但因为没有什么CPAN做这个(有东西在CPAN的一切),我开始怀疑:

有更好的方法吗?有没有特别的原因,例如Perl CacheCache::Cache类没有提供这样的机制?我可以使用一种经过验证的真实模式吗?

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

编辑:我已经知道这被称为缓存踩踏事件,并更新了问题的标题.

pil*_*row 2

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;
Run Code Online (Sandbox Code Playgroud)

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

风险:LOCK_EX 可能会永远阻塞,而且时间很长。避免 SIGSTOP,也许可以适应alarm().

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