Memoize::Expire - 用于自动过期记忆值的插件模块
use Memoize;
use Memoize::Expire;
tie my %cache => 'Memoize::Expire',
LIFETIME => $lifetime, # In seconds
NUM_USES => $n_uses;
memoize 'function', SCALAR_CACHE => [HASH => \%cache ];
Memoize::Expire 是 Memoize 的一个插件模块。它允许记忆函数的缓存值自动过期。本手册假设您已经熟悉 Memoize 模块。如果不是,您应该先仔细研究该手册,特别注意 HASH 特性。
Memoize::Expire 是一个软件层,您可以将其插入 Memoize 本身和实现缓存的任何底层包之间。该层呈现一个哈希变量,其值在过时、使用次数过多或两者兼有时都会过期。您可以告诉 Memoize
使用这个健忘的哈希作为它的缓存,而不是默认的普通哈希。
要指定实时超时,请使用LIFETIME
选项提供数值。缓存数据将在指定秒数后过期,并在过期时重新查找。当数据项被重新查找时,其生存期将被重置。
如果您使用n作为参数指定NUM_USES
,则每次访问缓存数据项n次后,该数据项将被丢弃并重新查找。当数据项被重新查找时,其使用次数将被重置。
如果您同时指定这两个参数,则当任一过期条件成立时,数据将从缓存中丢弃。
Memoize::Expire在内部使用真实哈希来存储缓存数据。您可以使用HASH
选项传递一个绑定哈希给Memoize::Expire,以代替Memoize::Expire通常使用的普通哈希。您可以使用此功能在持久磁盘哈希和Memoize之间添加Memoize::Expire作为一层。如果您这样做,您将获得一个持久磁盘缓存,其条目会自动过期。例如
# Memoize
# |
# Memoize::Expire enforces data expiration policy
# |
# DB_File implements persistence of data in a disk file
# |
# Disk file
use Memoize;
use Memoize::Expire;
use DB_File;
# Set up persistence
tie my %disk_cache => 'DB_File', $filename, O_CREAT|O_RDWR, 0666];
# Set up expiration policy, supplying persistent hash as a target
tie my %cache => 'Memoize::Expire',
LIFETIME => $lifetime, # In seconds
NUM_USES => $n_uses,
HASH => \%disk_cache;
# Set up memoization, supplying expiring persistent hash for cache
memoize 'function', SCALAR_CACHE => [ HASH => \%cache ];
Memoize::Expire并没有什么特别之处。它只是一个例子。如果您不喜欢它实现的策略,您可以自由地编写自己的过期策略模块,以实现您想要的任何策略。以下是操作方法。假设您的模块将命名为MyExpirePolicy。
简要概述:您需要创建一个包,定义四个方法
构造并返回缓存对象。
给定一个函数参数,对应的函数值是否在缓存中,如果是,它是否足够新鲜可以使用?
给定一个函数参数,在缓存中查找对应的函数值并返回它。
给定一个函数参数和对应的函数值,将它们存储到缓存中。
(可选) 彻底清空缓存。
想要根据您的策略使记忆缓存失效的用户,可以通过以下方式进行操作:
tie my %cache => 'MyExpirePolicy', args...;
memoize 'function', SCALAR_CACHE => [HASH => \%cache];
这将调用 MyExpirePolicy->TIEHASH(args)
。MyExpirePolicy::TIEHASH 应该执行必要的操作来设置缓存,并返回缓存对象给调用者。
例如,MyExpirePolicy::TIEHASH 可以创建一个包含常规 Perl 哈希表(用于存储缓存值)以及有关参数、数据年龄等额外信息的 对象。我们称此对象为 C
。
当 Memoize 需要检查缓存中是否已存在条目时,它将调用 C->EXISTS(key)
。key
是规范化的函数参数。MyExpirePolicy::EXISTS 应该在键不存在于缓存中或已过期时返回 0,在缓存中存在未过期的值时返回 1。它不应该返回 undef
,因为某些版本的 Perl 中存在一个错误,如果 EXISTS 方法返回 undef
,会导致错误的 FETCH。
如果您的 EXISTS 函数返回 true,Memoize 将尝试通过调用 C->FETCH(key)
来获取缓存值。MyExpirePolicy::FETCH 应该返回缓存值。否则,Memoize 将调用被记忆的函数来计算适当的值,并将该值存储到缓存中,方法是调用 C->STORE(key, value)
。
以下是一个非常简短的策略模块示例,该模块在十秒后使每个缓存项失效。
package Memoize::TenSecondExpire;
sub TIEHASH {
my ($package, %args) = @_;
my $cache = $args{HASH} || {};
bless $cache => $package;
}
sub EXISTS {
my ($cache, $key) = @_;
if (exists $cache->{$key} &&
$cache->{$key}{EXPIRE_TIME} > time) {
return 1
} else {
return 0; # Do NOT return undef here
}
}
sub FETCH {
my ($cache, $key) = @_;
return $cache->{$key}{VALUE};
}
sub STORE {
my ($cache, $key, $newvalue) = @_;
$cache->{$key}{VALUE} = $newvalue;
$cache->{$key}{EXPIRE_TIME} = time + 10;
}
要使用此失效策略,用户可以执行以下操作:
use Memoize;
tie my %cache10sec => 'Memoize::TenSecondExpire';
memoize 'function', SCALAR_CACHE => [HASH => \%cache10sec];
当缓存值完全不存在或超过十秒时,Memoize 将调用 function
。
您应该始终支持 TIEHASH
的 HASH
参数,该参数绑定底层缓存,以便用户可以指定缓存也是持久性的或具有其他有趣的语义。上面的示例演示了如何执行此操作,Memoize::Expire
也是如此。
另一个示例模块,Memoize::Saves,可在 CPAN 上的单独发行版中获得。它实现了一种策略,允许您指定某些函数值始终会重新查找。有关详细信息,请参阅文档。
Brent Powers 开发了一个名为 Memoize::ExpireLRU 的模块,它旨在与 Memoize 配合使用,并提供对最近最少使用数据的过期处理。缓存以固定数量的条目存储,当有新数据进来时,最不常用的数据将过期。
Joshua Chamas 的 Tie::Cache 模块可能用作过期管理器。(如果您尝试过,请告诉我结果如何。)
如果您开发了任何有用的过期管理器,您认为应该与 Memoize 一起分发,请告诉我。
此模块处于实验阶段,可能包含错误。请将错误报告到以下地址。
使用次数存储为 16 位无符号整数,因此不能超过 65535。
由于时钟粒度的原因,过期时间可能比您预期的时间早一秒。例如,假设您存储了一个生命周期为十秒的值,并且您在某一天的 12:00:00.998 存储了它。Memoize 会查看时钟并看到 12:00:00。然后 9.01 秒后,在 12:00:10.008,您尝试将其读回。Memoize 会查看时钟并看到 12:00:10,并得出结论认为该值已过期。如果您安装了 Time::HiRes
,则这种情况可能不会发生。
Mark-Jason Dominus
Mike Cariaso 为解决此问题的最佳方法提供了宝贵的见解。
perl(1)
Memoize 手册页。