Don*_*ato 4 ruby caching ruby-on-rails
Rails附带了Fragment Caching和Low-Level Caching.片段缓存的工作方式非常清楚:
Rails将使用唯一键编写新的缓存条目.如果updated_at的值已更改,则将生成新密钥.然后Rails会为该密钥写一个新的缓存,并且永远不会再次使用写入旧密钥的旧缓存.这称为基于密钥的过期.当视图片段改变时(例如,视图中的HTML改变),高速缓存片段也将过期.像Memcached这样的缓存存储将自动删除旧的缓存文件.
<% @products.each do |product| %>
<% cache product do %>
<%= render product %>
<% end %>
<% end %>
Run Code Online (Sandbox Code Playgroud)
因此,视图将被缓存,当与视图关联的ActiveRecord对象的updated_at发生更改或html发生更改时,将创建新缓存.可以理解的.
我不想缓存视图.我想缓存从ActiveRecord查询构建的Hash集合.我知道Rails有SQL缓存,它在一个请求中使用时缓存同一个查询的结果.但是我需要在多个请求中提供结果,并且只有在对象之一更新update_at或将新对象添加到Hash集合时才会更新.
低级缓存缓存特定值或查询结果,而不是缓存视图片段.
def get_events
@events = Event.search(params)
time = Benchmark.measure {
event_data = Rails.cache.fetch 'event_data' do
# A TON OF EVENTS TO LOAD ON CALENDAR
@events.collect do |event|
{
title: event.title,
description: event.description || '',
start: event.starttime.iso8601,
end: event.endtime.iso8601,
allDay: event.all_day,
recurring: (event.event_series_id) ? true : false,
backgroundColor: (event.event_category.color || "red"),
borderColor: (event.event_category.color || "red")
}
end
end
}
Rails.logger.info("CALENDAR EVENT LOAD TIME: #{time.real}")
render json: event_data.to_json
end
Run Code Online (Sandbox Code Playgroud)
但是现在我不认为如果其中一个事件被更新或者新的哈希被添加到集合中,缓存就会过期.我怎样才能做到这一点?
Pie*_*son 13
我们试图用缓存做的是这样的:
value = Rails.cache.read( :some_key )
if value.nil?
expected_value = ...
Rails.cache.write( :some_key, expected_value )
end
Run Code Online (Sandbox Code Playgroud)
我们首先尝试从缓存中读取,如果没有值,那么我们从任何地方检索数据,将其添加到缓存中并执行您需要执行的任何操作.
在下一次调用时,我们将尝试再次访问:some_key缓存键,但这次它将存在,我们不需要再次检索值,我们只需要获取已经缓存的值.
这就是fetch所有的一切:
value = Rails.cache.fetch( :some_key ) do
# if :some_key doesn't exist in cache, the result of this block
# is stored into :some_key and gets returned
end
Run Code Online (Sandbox Code Playgroud)
它只不过是一个非常方便的捷径,但了解它的行为很重要.
这里的关键(双关语)是选择一个缓存密钥,当缓存的数据与底层数据不是最新时,缓存密钥会发生变化.它通常比更新现有缓存值更容易:相反,您确保不重用已存储旧数据的缓存键.
例如,要将所有事件数据存储在缓存中,我们会执行以下操作:
def get_events
# Get the most recent event, this should be a pretty fast query
last_modified = Event.order(:updated_at).last
# Turns a 2018-01-01 01:23:45 datetime into 20180101220000
# We could use to_i or anything else but examples would be less readable
last_modified_str = last_modified.updated_at.utc.to_s(:number)
# And our cache key would be
cache_key = "all_events/#{last_modified_str}"
# Let's check this cache key: if it doesn't exist in our cache store,
# the block will store all events at this cache key, and return the value
all_events = Rails.cache.fetch(cache_key) do
Event.all
end
# do whatever we need to with all_events variable
end
Run Code Online (Sandbox Code Playgroud)
这里有什么重要的:
fetch块.每次进入此方法时都不能触发它,否则您将失去缓存的所有兴趣.让我们get_events通过一个例子来检查我之前的方法是如何表现的.假设我们Events在您的数据库中有以下内容:
| ID | Updated_at |
|----|------------------|
| 1 | 2018-01-01 00:00 |
| 2 | 2018-02-02 00:00 |
| 3 | 2018-03-03 00:00 |
Run Code Online (Sandbox Code Playgroud)
在这一点上,让我们来电话get_events.事件#3是最近更新的事件,因此Rails将检查缓存密钥all_events/20180303000000.它尚不存在,因此将从DB请求所有事件并使用此缓存密钥将其存储到缓存中.
如果您不更新任何这些事件,则所有下一次调用都get_events将访问all_events/20180303000000现在存在的缓存键并包含所有事件.因此,您不会点击数据库,只需使用缓存中的值.
Event.find(2).touch
Run Code Online (Sandbox Code Playgroud)
我们已经修改了事件#2,因此预先存储在缓存中的内容不再是最新的.我们现在有以下事件列表:
| ID | Updated_at |
|----|------------------|
| 1 | 2018-01-01 00:00 |
| 2 | 2018-04-07 19:27 | <--- just updated :)
| 3 | 2018-03-03 00:00 |
Run Code Online (Sandbox Code Playgroud)
下一次调用get_events将采用最近的事件(现在是#2),因此尝试访问all_events/20180407192700尚不存在的缓存键... Rails.cache.fetch将评估该块,并将当前状态中的所有当前事件放入此新密钥中all_events/20180407192700.并且您不会收到过时的数据.
您必须找到正确的缓存键,并使其在fetch块内完成事件数据加载.
由于您使用过滤事件params,缓存将取决于您的参数,因此您需要找到一种方法将params表示为字符串,以将其集成到您的缓存键.缓存事件将从一组参数到另一组参数不同.
查找这些参数的最近更新事件,以避免检索过时数据.我们可以cache_key在任何ActiveRecord对象上使用ActiveRecord 方法作为其缓存键,这很方便,并且像我们之前那样避免繁琐的时间戳格式化.
这应该给你这样的东西:
def get_events
latest_event = Event.search(params).order(:updated_at).last
# text representation of the given params
# Check https://apidock.com/rails/ActiveRecord/Base/cache_key
# for an easy way to define cache_key for an ActiveRecord model instance
cache_key = "filtered_events/#{text_rep_of_params}/#{latest_event.cache_key}"
event_data = Rails.cache.fetch(cache_key) do
events = Event.search(params)
# A TON OF EVENTS TO LOAD ON CALENDAR
events.collect do |event|
{
title: event.title,
description: event.description || '',
start: event.starttime.iso8601,
end: event.endtime.iso8601,
allDay: event.all_day,
recurring: (event.event_series_id) ? true : false,
backgroundColor: (event.event_category.color || "red"),
borderColor: (event.event_category.color || "red")
}
end
end
render json: event_data.to_json
end
Run Code Online (Sandbox Code Playgroud)
瞧!我希望它有所帮助.祝你的实施细节好运.