用于收集的低级缓存

Joh*_*ohn 5 caching ruby-on-rails redis

我想在我的Rails应用程序中使用Redis进行一些低级缓存.在控制器中,我通常使用它来获取所有书籍:

class BooksController < ApplicationController
  def index
    @books = Book.order(:title)
  end
end
Run Code Online (Sandbox Code Playgroud)

视图迭代了这个:

<ul>
  - @books.each do |book|
    <li>= "#{book.title} - #{book.author}"</li>
</ul>
Run Code Online (Sandbox Code Playgroud)

现在我想要完全相同的结果,然后缓存.我有Redis设置并运行.所以我应该cached_books在控制器中使用这样的方法:

@books = Book.cached_books.order(:title)
Run Code Online (Sandbox Code Playgroud)

保持视图原样,或者在视图中使用book.cached_titlebook.cached_author保持控制器原样?

一个cached_books方法在Book模型中会是什么样子?

class Book < ActiveRecord::Base
  ...

  def cached_books
    Rails.cache.fetch([self, "books"]) { books.to_a }
  end
end
Run Code Online (Sandbox Code Playgroud)

为了简单起见,我暂时忽略了过期策略,但显然他们需要在那里.

shl*_*jin 5

所以我应该在控制器中使用cached_books方法,如下所示:

是的你可以.虽然有一些问题你必须要注意. BookActiveRecord.当你调用时Book.something(例如Book.all,或者甚至只是Book.order(:title)它返回一个ActiveRecord::Relation,它基本上是Books 数组的包装器(这个包装器可以防止发出不必要的查询,提高性能).

您无法在Redis中保存查询的整个结果.比如,您可以使用模型属性保存哈希数组的JSON字符串,例如

[{
  id: 1,
  title: 'How to make a sandwich",
  author: 'Mr. cooker'
}, {
  id: 2,
  title: 'London's Bridge',
  author: 'Fergie'
}]
Run Code Online (Sandbox Code Playgroud)

然后你可以将这个东西"解密"到数组之后.就像是

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
Run Code Online (Sandbox Code Playgroud)

而且,在将属性放入缓存之前,您必须序列化属性.

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

嗯......值得吗?实际上,不是真的.如果你需要缓存这篇文章 - 最好的方法是缓存一个渲染的页面,而不是一个查询结果.

如果您使用cached_titlecached_author-这是很好的问题.首先,它取决于cached_title可能的内容.如果它是一个字符串 - 没有什么可以缓存.你得到一个Book直通数据库请求,或者你Book从缓存中得到- title它将以任何方式呈现在它中,因为它是一个简单的类型.但让我们看看更接近author.最有可能的是它与另一个模型的关系Author,这就是缓存非常适合的地方.你可以author在书中重新定义方法(或者定义新的并避免Rails在将来的复杂查询中可能会产生令人讨厌的影响)并查看是否有缓存.如果是,请返回缓存.如果不是 - 查询数据库,将结果保存到缓存并返回它.

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
Run Code Online (Sandbox Code Playgroud)

或者不太方便,但更"安全":

def cached_author
  Rails.cache.fetch("#{author_id}/info", expires_in: 12.hours) do
    author
  end
end
Run Code Online (Sandbox Code Playgroud)