1

我有一张languages几乎没有任何变化的桌子。我试图避免在这个表上进行数据库查询,而不是初始缓存。

class Language < ActiveRecord::Base
  attr_accessible :code, :name, :native_name

  def self.find_by_name_in_cache(name)
    get_all_cached.find {|l| l.name == name}
  end

  def self.find_by_code_in_cache(code)
    get_all_cached.find {|l| l.code == code}
  end

  def self.find_by_id_in_cache(id)
    get_all_cached.find {|l| l.id == id}
  end

  def self.get_all_cached
    Rails.cache.fetch('all_languages') {Language.all}
  end
end

只要我使用find_in_cache我定义的方法之一,一切都会很好。

我的问题是,我怎样才能强制ActiveRelation使用缓存。

例如,考虑以下用户模型:

class User < ActiveRecord::Base
  belongs_to :native_language, :class_name => :Language, :foreign_key => :native_language_id
end

当我访问@user.native_language时,它language从数据库中查询。我非常感谢任何防止这种情况发生的想法。

我知道我可以做到以下几点:

class User < ActiveRecord::Base
  belongs_to :native_language, :class_name => :Language, :foreign_key => :native_language_id

  def native_language_cached
    Language.find_by_id_in_cache(self.native_language_id)
  end
end

但是,我希望有一个更透明的解决方案,因为我的很多表都引用了表,将这些方法添加到所有这些模型中languages会很麻烦。cached

4

1 回答 1

1

ActiveRecord::QueryCache要在单个请求期间缓存查询,除了确保使用中间件外,您无需执行任何操作。如果你调用它两次:

2.times{ user.native_language.to_s }

您会在日志中看到类似的内容:

Language Load (0.2ms)   SELECT `languages`.* FROM `languages` WHERE ...
CACHE (0.0ms)  SELECT `languages`.* FROM `languages` WHERE ...

跨请求缓存需要手动缓存。identity_cache gem 可能对您尝试做的事情有用(缓存关联)。

最快的方法可能只是将expires_in选项添加到您拥有的代码中。您可以编写如下通用cached_find方法:

def self.cached_find(id)
  key = model_name.cache_key + '/' + id
  Rails.cache.fetch(key) do
    find(id).tap do |model|
      Rails.cache.write(key, model, expires_in: cache_period)
    end
  end
end

def self.cache_period
  3.days
end

您可以使其成为一个模块 mixin,以根据需要与尽可能多的模型一起使用。这确实意味着您必须编写自己的关联发现。您也可以使用回调来做到这一点:

after_commit do
  Rails.cache.write(cache_key, self, expires_in: self.class.cache_period)
end
于 2013-09-23T20:02:31.360 回答