Rails附带了片段缓存和低级别缓存。很清楚片段缓存是如何工作的:
Rails将用唯一的键编写一个新的缓存条目。如果updated_at的值发生了更改,将生成一个新的键。然后Rails将向该键写入一个新的缓存,而写入旧键的旧缓存将不再被使用。这被称为基于密钥的过期。当视图片段更改时(例如视图中的HTML ),缓存片段也将过期。像Memcached这样的缓存存储将自动删除旧的缓存文件。
<% @products.each do |product| %>
<% cache product do %>
<%= render product %>
<% end %>
<% end %>因此,视图将被缓存,当与视图关联的ActiveRecord对象的ActiveRecord更改或html更改时,就会创建新的缓存。可以理解。
我不想缓存视图。我想缓存从ActiveRecord查询生成的哈希集合。我知道Rails有SQL缓存,当在一个请求中使用时,它会缓存相同查询的结果。但是,我需要多个请求之间可用的结果,并且只有当一个对象或一个新对象的updated_at更改到哈希集合时才会更新。
低级缓存缓存特定值或查询结果,而不是缓存视图片段。
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但是现在,如果更新了其中一个事件或者向集合添加了一个新哈希,那么缓存就不会过期。我该怎么做?
发布于 2018-04-07 18:09:51
Rails.cache.fetch是读写的快捷方式。
我们试图对缓存做的是:
value = Rails.cache.read( :some_key )
if value.nil?
expected_value = ...
Rails.cache.write( :some_key, expected_value )
end我们首先尝试从缓存中读取数据,如果不存在值,则从它所在的位置检索数据,将其添加到缓存中,并对其做任何需要做的事情。
在接下来的调用中,我们将尝试再次访问: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这不过是一条非常方便的捷径,但了解它的行为很重要。
我们如何处理低级别缓存?
此处的关键(双关意)是选择一个缓存键,当缓存的数据与底层数据不同步时,该键会发生更改。它通常比更新现有的缓存值更容易:相反,要确保在存储旧数据的地方不重用缓存密钥。
例如,要将所有事件数据存储在缓存中,我们可以执行如下操作:
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这里真正重要的是:
fetch块内部。不能每次进入此方法时都触发它,否则就会失去缓存的所有兴趣。
使用前面的方法的一个示例
让我们通过一个示例来检查我以前的get_events方法的行为。假设您的DB中有以下Events:
| ID | Updated_at |
|----|------------------|
| 1 | 2018-01-01 00:00 |
| 2 | 2018-02-02 00:00 |
| 3 | 2018-03-03 00:00 |第一呼叫
现在,让我们调用get_events。事件3是最近更新的事件,因此Rails将检查缓存键all_events/20180303000000。它还不存在,所以所有事件都将从DB请求并用这个缓存键存储到缓存中。
相同的数据,随后的调用
如果您不更新这些事件中的任何一个,那么对get_events的所有下一次调用都将命中缓存键all_events/20180303000000,该键现在存在并包含所有事件。因此,您不会访问DB,只需使用缓存中的值。
如果我们修改一个事件呢?
Event.find(2).touch我们已经修改了事件#2,所以缓存中存储的previoulsy已经不再是最新的了。我们现在有以下事件列表:
| ID | Updated_at |
|----|------------------|
| 1 | 2018-01-01 00:00 |
| 2 | 2018-04-07 19:27 | <--- just updated :)
| 3 | 2018-03-03 00:00 |下一次对get_events的调用将接受最近的事件(现在#2),因此尝试访问缓存键all_events/20180407192700.Rails.cache.fetch将对块进行评估,并将处于当前状态的所有当前事件放入新的键all_events/20180407192700中。你也不会得到过时的数据。
那你的问题呢?
您必须找到适当的缓存键,并使其能够在fetch块内完成事件数据加载。
由于使用params筛选事件,缓存将取决于params,因此需要找到一种方法将params表示为字符串,以便将其集成到缓存键中。缓存事件将因一组params而异。
为这些参数查找最近更新的事件,以避免检索陈旧的数据。我们可以在任何ActiveRecord对象上使用cache_key方法作为缓存键,这非常方便,并且避免了像以前那样繁琐的时间戳格式设置。
这应该会给你这样的东西:
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哇哦!希望能帮上忙。祝您的实现细节好运。
https://stackoverflow.com/questions/49699795
复制相似问题