在 C#/.Net 世界中,有诸如 NHibernate 或 ActiveRecord 之类的 ORM 包括透明缓存:数据库更新透明地复制到缓存中,对象在可用时直接从缓存中检索,等等(通常使用 memcached)。
看起来 Perl 中没有透明缓存与DBIx::Class可用。我错过了什么?这似乎是一个普遍的需求,我很惊讶我在 CPAN 或 Google 上找不到任何东西。
在 C#/.Net 世界中,有诸如 NHibernate 或 ActiveRecord 之类的 ORM 包括透明缓存:数据库更新透明地复制到缓存中,对象在可用时直接从缓存中检索,等等(通常使用 memcached)。
看起来 Perl 中没有透明缓存与DBIx::Class可用。我错过了什么?这似乎是一个普遍的需求,我很惊讶我在 CPAN 或 Google 上找不到任何东西。
半透明地有 DBIx::Class::Cursor::Cached (来自 mst,如 DBIC)。不过,您需要为您的连接或模式对象提供一个 Cache 对象。不幸的是,似乎非常无证。
Cookbook 确实有一个在 DBIC 上使用 Tie::Cache 的示例,并且在 DBIx::Class::ResultSet 上也有 (get|set|clear)_cache 函数,但它们可能并不完全符合您的需要。
这是一种简单的方法,您可以使用CHI添加缓存。我实际上并没有尝试过这个,所以可能有我没有考虑过的陷阱,尤其是在 DBIC 结果集的序列化方面。
package My::Table;
use strict;
use warnings;
use base 'DBIx::Class';
use Storable 'freeze';
use CHI;
$Storable::canonical = 1;
__PACKAGE__->load_components(qw/Core/);
__PACKAGE__->table('mytable');
# ....
my $CACHE = CHI->new( driver => 'Memory' );
sub search {
my $self = shift;
my $key = freeze( \@_ ); # make cache key from params
if ( my $rs = $CACHE->get( $key ) ) {
return $rs;
}
# Note: there are issues with context propagation here
my $rs = $self->next::method( @_ );
$CACHE->set( $key => $rs );
return $rs;
}
sub update {
my $self = shift;
my @keys = $self->find_all_cache_items_affected_by_this_update( @_ );
$CACHE->remove( $_ ) for @keys;
$self->next::method( @_ );
}
这有点笨拙,但我认为这是一个很好的起点。如果您在所有 DBIx::Class 表类的基类中执行此类操作,您应该能够非常轻松地构建透明缓存。
我在基于 DBIx::Class 的模型中遇到了同样的需求,在查看了这里的答案后,我并没有真正看到任何我正在寻找的解决方案。在解决了这个问题之后,我开始认为我的业务层应该处理缓存,因此我将 DBIx::Class 视为不实现业务逻辑的持久层。
例如,我当前具有理想缓存的代码将是这样的:
my $network = SL::Model::App->resultset('Network')->search({ ip => '127.0.0.1' });
$network 对象由我在 DBIx::Class 模式初始化期间配置的 $memcached 缓存提供服务
新代码将是:
my $network = SL::Network->find_by_ip_or_create({ ip => '127.0.0.1' });
同时,在附近的模块中:
package SL::Network;
...
use SL::Model::App;
use SL::Cache;
our $cache = SL::Cache->new;
sub find_by_ip_or_create {
my ($class, $args) = @_;
my $network;
unless ($network = $cache->get('network|' . $args->{ip}) {
$network = SL::Model::App->resultset('Network')->find_or_create({ wan_ip => $args->{ip}});
$cache->set('network|' . $args->{ip} => DBIx::Class::Schema->freeze($network));
}
return $network;
}
你明白了。
我想补充一点,而不是在 中添加“搜索”方法My::Table
,
还可以增强 提供的 ->search 方法DBIx::Class::ResultSet
,如下所示:
package Schema::ResultSet::My::Table;
use base 'DBIx::Class::ResultSet';
sub search {
my ( $self, $args ) = ( shift, shift );
# do what you want here with the args passed to ->search
return $self->next::method( $args, @_ );
}
此外,您很可能将 ResultSet 子类化,因此您可以为所有 ResultSet 提供这种更改(缓存)的搜索,从而将所有表的缓存代码保存在一个地方,恕我直言,这会少很多混乱。
不过,我还没有测试过。
为了使上面的例子工作,把它放在一个文件中,用你的模式类的名称,在目录"../Schema/ResultSet/"
中,并确保你的 Schema.pm 包含"load_namespaces();"
它将很好地自动加载你放在那里的所有重载类(我认为我的Catalyst install 是自动完成的,但我不记得了)。