我们有一个非常昂贵的计算,我们想缓存.所以我们做类似的事情:
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 Cache和Cache::Cache类没有提供这样的机制?我可以使用一种经过验证的真实模式吗?
理想的是一个带有debian软件包的CPAN模块已经处于挤压或尤里卡时刻,在那里我看到了我的方式的错误...... :-)
编辑:我已经知道这被称为缓存踩踏事件,并更新了问题的标题.
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。