【问题标题】:Confusion caching Active Record queries with Rails.cache.fetch使用 Rails.cache.fetch 混淆缓存 Active Record 查询
【发布时间】:2016-06-02 19:12:29
【问题描述】:

我的版本是:

  • 导轨:3.2.6
  • 达利:2.1.0

我的环境是:

  • config.action_controller.perform_caching = true
  • config.cache_store = :dalli_store, 'localhost:11211', {:namespace => 'MyNameSpace'}

当我写作时:

 Rails.cache.fetch(key) do
     User.where('status = 1').limit(1000)
 end

无法缓存用户模型。如果我使用

 Rails.cache.fetch(key) do
     User.all
 end

它可以被缓存。如何缓存查询结果?

【问题讨论】:

    标签: ruby-on-rails caching


    【解决方案1】:

    使用

    User.where("status = 1").limit(1000).all
    

    应该可以。

    【讨论】:

      【解决方案2】:

      Rails.cache.fetch 准确缓存块的计算结果。

       User.where('status = 1').limit(1000)
      

      只是一个作用域,所以缓存的只是 ActiveRecord::Relation 对象,即查询,而不是它的结果(因为查询尚未执行)。

      如果你想缓存一些有用的东西,你需要在块内强制执行查询,例如通过做

      User.where('status = 1').limit(1000).all
      

      请注意,在 rails 4 上,all 不会强制加载关系 - 请改用 to_a

      【讨论】:

        【解决方案3】:

        原因是因为

        User.where('status = 1').limit(1000)
        

        返回一个ActiveRecord::Relation,它实际上是一个作用域,而不是一个查询。 Rails 缓存范围。

        如果要缓存查询,需要在末尾使用查询方法,如#all

        Rails.cache.fetch(key) do
          User.where('status = 1').limit(1000).all
        end
        

        请注意,缓存 ActiveRecord 对象从来都不是一个好主意。缓存对象可能会导致不一致的状态和值。如果适用,您应该始终缓存原始对象。在这种情况下,请考虑缓存 id。

        ids = Rails.cache.fetch(key) do
          User.where('status = 1').limit(1000).pluck(:id)
        end
        User.find(ids)
        

        您可能会争辩说,在这种情况下,对User.find 的调用总是会被执行。确实如此,但是使用主键的查询速度很快,并且您可以解决我之前描述的问题。此外,缓存活动记录对象可能会很昂贵,并且您可能很快就会用一个缓存条目填满所有 Memcached 内存。缓存 id 也可以防止这个问题。

        【讨论】:

        • 谢谢。如果我有一个类别模型,它可能只有不到 100 条记录,我缓存 Category.all 是个好主意吗?不缓存类别ID
        • 或者我先把Category.all改成json,然后缓存到memcached中?
        • 这是一个非常重要和微妙的问题;我们刚刚意识到这个问题已经蔓延到我们的应用程序中——Foo.where(bar: 'fubar')Foo.where(bar: 'fubar').all 之间的区别是完全不同的 w/r/t 缓存,并且可以轻松巧妙地进入您的代码库。如果您依赖缓存,请定期检查和检查。
        • 很好地解释了为什么缓存可能并不总是一个好主意。我遇到的一个问题是,除非您在迁移过程中明确使缓存对象过期,否则在迁移期间不会升级缓存结果。例如,如果您添加一个迁移,该迁移添加了一个具有默认值的新字段并传入:null => false(有效地使用默认值升级表中的所有行),缓存中的记录将返回一个 nil 值,而不是默认情况下,直到您过期缓存记录并强制再次获取它们。
        • 我想要更多证据表明缓存 ActiveRecords 从不是一个好主意(尽管正如您所指出的那样,find(ids) 的替代品无论如何都相当快)。特别是如果它们被只读用于显示目的——也许是最常见的用法——我不确定它在大多数情况下会造成多大的伤害。如果关联发生变化,可能存在链接断开的风险,需要进行一些个案分析,但我认为这在现实中很少见。
        【解决方案4】:

        除了选择的答案:对于 Rails 4+,您应该使用 load 而不是 all 来获取范围的结果。

        【讨论】:

        • User.find(1).load 在 Rails 4.1.15 上给我ArgumentError: wrong number of arguments (given 0, expected 1..2)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-08
        • 2011-10-22
        • 2013-02-27
        相关资源
        最近更新 更多