首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Rails低级别缓存:当ActiveRecord对象updated_at更改时更新缓存,或向集合中添加新对象时更新缓存

Rails低级别缓存:当ActiveRecord对象updated_at更改时更新缓存,或向集合中添加新对象时更新缓存
EN

Stack Overflow用户
提问于 2018-04-06 19:27:28
回答 1查看 9.2K关注 0票数 9

Rails附带了片段缓存和低级别缓存。很清楚片段缓存是如何工作的:

Rails将用唯一的键编写一个新的缓存条目。如果updated_at的值发生了更改,将生成一个新的键。然后Rails将向该键写入一个新的缓存,而写入旧键的旧缓存将不再被使用。这被称为基于密钥的过期。当视图片段更改时(例如视图中的HTML ),缓存片段也将过期。像Memcached这样的缓存存储将自动删除旧的缓存文件。

代码语言:javascript
运行
复制
<% @products.each do |product| %>
  <% cache product do %>
   <%= render product %>
  <% end %>
<% end %>

因此,视图将被缓存,当与视图关联的ActiveRecord对象的ActiveRecord更改或html更改时,就会创建新的缓存。可以理解。

我不想缓存视图。我想缓存从ActiveRecord查询生成的哈希集合。我知道Rails有SQL缓存,当在一个请求中使用时,它会缓存相同查询的结果。但是,我需要多个请求之间可用的结果,并且只有当一个对象或一个新对象的updated_at更改到哈希集合时才会更新。

低级缓存缓存特定值或查询结果,而不是缓存视图片段。

代码语言:javascript
运行
复制
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

但是现在,如果更新了其中一个事件或者向集合添加了一个新哈希,那么缓存就不会过期。我该怎么做?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-04-07 18:09:51

Rails.cache.fetch是读写的快捷方式。

我们试图对缓存做的是:

代码语言:javascript
运行
复制
value = Rails.cache.read( :some_key )
if value.nil?
  expected_value = ...
  Rails.cache.write( :some_key, expected_value )
end

我们首先尝试从缓存中读取数据,如果不存在值,则从它所在的位置检索数据,将其添加到缓存中,并对其做任何需要做的事情。

在接下来的调用中,我们将尝试再次访问:some_key缓存键,但这一次它将存在,我们不需要再次检索值,只需要获取已经缓存的值。

这就是fetch同时做的事情:

代码语言:javascript
运行
复制
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

这不过是一条非常方便的捷径,但了解它的行为很重要。

我们如何处理低级别缓存?

此处的关键(双关意)是选择一个缓存键,当缓存的数据与底层数据不同步时,该键会发生更改。它通常比更新现有的缓存值更容易:相反,要确保在存储旧数据的地方不重用缓存密钥。

例如,要将所有事件数据存储在缓存中,我们可以执行如下操作:

代码语言:javascript
运行
复制
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

代码语言:javascript
运行
复制
| 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,只需使用缓存中的值。

如果我们修改一个事件呢?

代码语言:javascript
运行
复制
Event.find(2).touch

我们已经修改了事件#2,所以缓存中存储的previoulsy已经不再是最新的了。我们现在有以下事件列表:

代码语言:javascript
运行
复制
| 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方法作为缓存键,这非常方便,并且避免了像以前那样繁琐的时间戳格式设置。

这应该会给你这样的东西:

代码语言:javascript
运行
复制
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

哇哦!希望能帮上忙。祝您的实现细节好运。

票数 33
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49699795

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档