【问题标题】:Does DBIx::Class have transparent caching?DBIx::Class 有透明缓存吗?
【发布时间】:2009-08-12 18:04:44
【问题描述】:

在 C#/.Net 世界中,诸如 NHibernate 或 ActiveRecord 之类的 ORM 包含透明缓存:数据库更新透明地复制到缓存中,对象在可用时直接从缓存中检索,等等(通常使用 memcached)。

在 Perl 中,DBIx::Class 似乎不提供透明缓存。我错过了什么?这似乎是一种常见的需求,我很惊讶在 CPAN 或 Google 上找不到任何内容。

【问题讨论】:

标签: perl caching dbix-class


【解决方案1】:

半透明地有 DBIx::Class::Cursor::Cached(来自 mst,如 DBIC)。不过,您需要为您的连接或模式对象提供一个 Cache 对象。不幸的是,似乎非常无证。

Cookbook 确实有一个在 DBIC 上使用 Tie::Cache 的示例,并且在 DBIx::Class::ResultSet 上也有 (get|set|clear)_cache 函数,但它们可能并不完全符合您的需要.

【讨论】:

  • 据我了解,这是一个非常有限的用例的缓存。如果您进行搜索,并多次检查搜索中的项目,您可以缓存结果集。但是您不能缓存单个对象。我已经测试过了,在我的情况下它根本没有帮助(实际上我的性能更差,因为我缓存了搜索结果,但只使用了一次)
  • 其实,这可能是一个解决方案。问题出在关系上:主要结果集被缓存,而不是关系。
【解决方案2】:

这是一种使用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 表类的基类中执行此类操作,您应该能够非常轻松地构建透明缓存。

【讨论】:

  • 谢谢,看起来不错。我将 Catalyst 与 Catalyst::Model::DBIC::Schema 一起使用,并且我认为找不到覆盖搜索方法的正确位置,例如:我在 DBIx::lass 和 DBIx::Class::Schema 中尝试过,但是使用 $c->model(''')->search 并没有使用新的搜索方法
  • 以防万一:搜索是一种结果集方法,这就是您需要覆盖它的地方。例如。覆盖 ResultSet::Foo->search,或者在加载模式时简单地设置一个默认的结果集类,并在所有自定义的结果集类中扩展它。
【解决方案3】:

我的基于 DBIx::Class 的模型也遇到了同样的需求,在查看了此处的答案后,我并没有真正看到任何我正在寻找的解决方案。在解决了这个问题之后,我开始认为我的业务层应该处理缓存,因此我将 DBIx::Class 视为不实现业务逻辑的持久层。

例如,我当前具有理想缓存的代码将是这样的:

my $network = SL::Model::App->resultset('Network')->search({ ip => '127.0.0.1' });

$network 对象是从我在配置期间配置的 $memcached 缓存中提供的 DBIx::类模式初始化

新代码是:

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;

}

你明白了。

【讨论】:

    【解决方案4】:

    我想补充一下,而不是在My::Table 中添加“搜索”方法,

    还可以增强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 安装是自动完成的,但我不记得了)。

    DBIx::Class::ResultSet

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-07-09
      • 1970-01-01
      • 2014-09-01
      • 2016-08-25
      • 1970-01-01
      • 2013-01-21
      • 2013-03-08
      相关资源
      最近更新 更多