caching_with_rails 中介绍了 Rails 缓存的相关内容;
我结合工作中的使用写下了本篇文章; 部分内容翻译自 rails 的官方文档;
缓存(cache)是一种提高应用性能的高效方式;
通过缓存, 应用可以在一台服务器和单一的数据库下, 维持上千的并发用户;
Rails 提供了一系列开箱即用的功能;
掌握这些技术, 你的 Rails 应用可以服务上百网的浏览器, 并且不需要过高的响应事件和服务器要求;
注意: rails 的缓存默认只有在 production
下才会生效;
示例代码, 需要在 production 下执行;
# 指定使用 production
rails c -e production
因为工作中很少用到, 因此省去了页面缓存的部分内容;
Rails 提供了一个 low-level caching 的 api; 开箱即用;
Rails.cache.fetch
第一个参数 key, 第二参数 hash; expires_in 表示过期时间;
当 fetch 不到值时, 才会执行 block;
class Product < ApplicationRecord
def competing_price
Rails.cache.fetch("#{cache_key_with_version}/competing_price", expires_in: 12.hours) do
puts '没有值的时候, 才会执行'
Competitor::API.find_price(id)
end
end
end
注意: 上面的代码中, 我们使用到了
cache_key_with_version
, 这个方法会返回例如products/233-20140225082222765838000/competing_price
; 会根据 model 的类名, id, 和 update_at 属性来生成;
使用场景:
查询缓存是 Rails 的一个重要的功能;
假如 rails 在同一个请求
中遇到了一样的查询, 它将使用缓存的结果; 而不是再去请求一次数据库;
原因是:
数据存储在数据库中, 每一次请求数据库需要读取硬盘, IO 时间较长;
直接读取内存中的数据, 会快很多;
代码案例:
class ProductsController < ApplicationController
def index
# 去数据库查询
@products = Product.all
# 直接取 内存中的结果
@products = Product.all
end
end
注意: 将数据库的数据缓存到内存中, 只存在于一个请求的生命周期中;
如果想要每次获取都取数据库的最新数据, 可以使用ActiveRecord::Base.uncached
ActiveRecord::Base.uncached do
Post.all.to_a # to_a to force DB query
end
rails 的 model association
都内置了缓存;
例如: belongs_to, has_one, has_many…
一般情况下, association 的缓存可以提高运行效率. 避免再去数据库中读取数据;
但是, 偶尔也会给我们带来麻烦;
在文档 controlling-caching中有提到的案例:
# 第一次获取, 从数据库中读取
author.books.load
# 之后, 再读取, 直接从内存中读取;
# 此时, 如果数据库的值变化,则会与内存中的不一致
author.books.size
# uses the cached copy of books
author.books.empty?
如果, 我们想要刷新缓存, 重新从数据库中获取最新值.
只需要执行reload
即可;
# retrieves books from the database
author.books.load
# uses the cached copy of books
author.books.size
# 如果对应的books有改动, 需要 reload
# discards the cached copy of books and goes back to the database
author.books.reload.empty?
options-for-belongs-to-counter-cache
:counter_cache
是 belongs_to 关系的一个 option.
它可以将所属对象的数量进行缓存;
class Book < ApplicationRecord
belongs_to :author
end
class Author < ApplicationRecord
has_many :books
end
上述代码, 如果我们想要知道一个 author 有多少 books 时, 需要执行select count(*)
的 sql 语句;
为了避免去数据库查询, 我们可以添加一个 counter_cache
class Book < ApplicationRecord
# 默认对应的字段名称为 books_count
belongs_to :author, counter_cache: true
# 可以指定 count_of_books
# belongs_to :author, counter_cache: :count_of_books
end
class Author < ApplicationRecord
# Author 表里需要有一个字段 books_count
has_many :books
end
上述案例中, 我们需要在Author
中添加一个books_count
的字段; 也可以指定字段名;
Rails 会在book
创建或者不在属于 author 时进行更新;
Rails 会在回调中执行更新 counter, 所以有一些不执行回调的方法, 例如: #delete
则不会更新;
注意: counter_cache 仅能在 belongs_to 下使用;
使用场景: