低级别缓存用于集合

5

我希望在我的Rails应用中使用Redis进行低级别的缓存。

在控制器中,我通常使用以下代码来获取所有图书:

class BooksController < ApplicationController
  def index
    @books = Book.order(:title)
  end
end

这个视图对此进行迭代:

<ul>
  - @books.each do |book|
    <li>= "#{book.title} - #{book.author}"</li>
</ul>

现在我希望得到完全相同的结果,但是要进行缓存。我已经设置并运行了Redis。所以我应该在控制器中使用一个cached_books方法,像这样:

@books = Book.cached_books.order(:title)

保留视图不变,或在视图中使用book.cached_titlebook.cached_author,并保留控制器不变?

Book模型中的cached_books方法会是什么样子?

class Book < ActiveRecord::Base
  ...

  def cached_books
    Rails.cache.fetch([self, "books"]) { books.to_a }
  end
end

出于简化的目的,我现在不考虑过期策略,但显然它们是必须存在的。


我不会在模型中添加缓存。 - apneadiving
只是确认一下:你有阅读过关于缓存的指南吗? - zwippie
当然可以,但我正在寻找针对集合的特定模型缓存。 - John
1个回答

6
所以我应该像这样在控制器中使用cached_books方法吗?
是的,你可以。但是你需要注意一些问题。Book是ActiveRecord。当你调用Book.something(例如Book.all,或者甚至只是Book.order(:title)),它会返回一个ActiveRecord :: Relation,这基本上是一个包装了Book数组的包装器(这个包装器防止触发不必要的查询,提高性能)。
你不能将整个查询结果保存在Redis中。比如说,你可以保存一个带有模型属性哈希数组的JSON字符串。
[{
  id: 1,
  title: 'How to make a sandwich",
  author: 'Mr. cooker'
}, {
  id: 2,
  title: 'London's Bridge',
  author: 'Fergie'
}]

然后您可以将此内容“解密”为数组。类似于:
def cached_books(key)
  # I suggest you to native wrapper
  if result = $redis.hget 'books_cache', key
    result.map do { |x| Book.new(x) }
  end
end

此外,在将属性放入缓存之前,您需要对其进行序列化。

好的,现在您有了可以在视图中迭代相同数据的集合,尽管您不能在缓存集合上调用order,因为它是一个普通的数组(您可以调用sort,但是想法是缓存已排序的数据)。

那么...这是否值得呢?实际上并不是。如果您需要缓存此部分-可能最好的方法是缓存已呈现的页面,而不是查询结果。

应该使用cached_titlecached_author - 这是个好问题。首先,这取决于cached_title可能是什么。如果它是一个字符串-没有什么可以缓存的。您通过DB请求获取Book或通过缓存获取Book-无论哪种方式,title都将以它出现,因为它是一个简单类型。但让我们更仔细地看一下author。最有可能它会成为另一个模型Author的关系,而这正是缓存非常适合的地方。您可以重新定义书籍内的author方法(或定义新的并避免将来复杂查询中Rails可能具有的不良影响),并查看是否存在缓存。如果是,则返回缓存。如果没有-查询DB,将结果保存到缓存中并返回它。

def author
  Rails.cache.fetch("#{author_id}/info", expires_in: 12.hours) do
    # block executed if cache is not founded
    # it's better to alias original method and call it here
    #instead of directly Author.find call though
    Author.find(author_id) 
  end
end

或者不太方便,但更“安全”的方法:
def cached_author
  Rails.cache.fetch("#{author_id}/info", expires_in: 12.hours) do
    author
  end
end

感谢您的全面解释,这对我非常有用! - John

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接