我想在我的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_title
和book.cached_author
并保持控制器原样?
cached_books
模型中Book
方法的外观如何?
class Book < ActiveRecord::Base
...
def cached_books
Rails.cache.fetch([self, "books"]) { books.to_a }
end
end
为简单起见,我现在忽略了过期策略,但显然他们需要在那里。
答案 0 :(得分:5)
所以我应该在控制器中使用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'
}]
然后你可以解密&#39;这个东西进入数组之后。像
这样的东西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
,因为它是一个普通数组(你可以调用{{ 1}},但想法是缓存已经排序的数据。
你应该使用sort
和cached_title
- 这是个好问题。首先,它取决于cached_author
可能是什么。如果它是一个字符串 - 你可以缓存任何东西。您可以通过数据库请求获得cached_title
,或者从缓存中获得Book
- Book
将以任何方式显示在其中,因为它是一种简单类型。但是让我们更接近title
。最有可能的是它与另一个模型author
的关系,这就是缓存非常适合的地方。您可以在本书中重新定义Author
方法(或者定义新的并避免Rails在将来的复杂查询中可能会产生令人讨厌的影响)并查看是否存在缓存。如果是,请返回缓存。如果不是 - 查询数据库,将结果保存到缓存并返回。
author
或不太方便,但更安全&#34;:
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