Rails设计:我如何(mem)缓存设计用户对象的数据库请求?

每次我访问经过身份validation的页面时,我都会注意到发布了一条SQL语句:

用户负载(0.2ms)SELECT users 。* FROM users WHERE( users id = 1)LIMIT 1

(我正在使用Rails 3 btw ..所以cache_money似乎是一个解决方案,尽管有很多搜索我找不到替代品)。

我在用户模型中尝试了很多覆盖,似乎只调用了find_by_sql。 哪个传递了整个SQL语句的字符串。 像find_by_id或find这样直观的东西似乎没有被调用。 我可以’覆盖这个方法并收集用户ID并从中做一个合理的缓存系统 – 但这非常难看。

我也尝试重写authenticate_user,我可以拦截一次SQL尝试但是然后调用current_user似乎再次尝试。

简单地说,我的用户对象很少变化,并且它是一个悲伤的状态,以保持为此而不是内存缓存解决方案。 (假设我愿意承担所有责任,使用以下内容来缓冲所述缓存:after_save作为部分但不是所有解决方案)

以下代码将通过其id缓存用户,并在每次修改后使缓存无效。

 class User < ActiveRecord::Base after_save :invalidate_cache def self.serialize_from_session(key, salt) single_key = key.is_a?(Array) ? key.first : key user = Rails.cache.fetch("user:#{single_key}") do User.where(:id => single_key).entries.first end # validate user against stored salt in the session return user if user && user.authenticatable_salt == salt # fallback to devise default method if user is blank or invalid super end private def invalidate_cache Rails.cache.delete("user:#{id}") end end 

警告:最有可能采用更好/更智能的方法。

几个月前我把这个问题赶了出来。 我发现 – 或者至少,我认为我发现 – Devise在这里加载用户对象: https : //github.com/plataformatec/devise/blob/master/lib/devise/rails/warden_compat.rb#L31

我在/initializers/warden.rb中为该反序列化方法创建了一个猴子补丁来进行缓存提取而不是获取。 它感到肮脏和错误,但它确实有效。

我也一直在努力解决这个问题。

这种不太复杂的方法是将此类方法添加到User模型中:

 def self.serialize_from_session(key, salt) single_key = key.is_a?(Array) ? key.first : key Rails.cache.fetch("user:#{single_key}") { User.find(single_key) } end 

请注意,我将模型名称添加到传入的对象ID中,以便从缓存中存储/检索对象; 你可以使用适合你需要的任何方案。

当然,唯一需要担心的是当某些内容发生变化时,缓存中的用户无效。 如果使用会话ID作为密钥的一部分将User存储在缓存中会很好,但会话在模型类中不可用,并且不会由Devise传递给此方法。