缓存有限的关联

mah*_*off 5 activerecord caching ruby-on-rails ruby-on-rails-3

我正在尝试使用其关联缓存ActiveRecord.问题是访问检索记录上的关联时存在数据库查询.

通常情况下,我只是缓存加载Rails.cache.write('post', Post.includes(:comments).find(99)).这似乎有效,但问题是我只想缓存关联的有限子集,并且在急切加载时会忽略限制(例如这里提到).因此,Post.includes(:popular_comments).find(99)将返回所有评论,而不仅仅是流行的评论.

所以我在懒惰加载关联后尝试缓存对象,但是在拉出对象时不幸发生了一个查询:

class Post < ActiveRecord::Base
  has_many :comments
  has_many :popular_comments, :class_name > 'Comment', :limit => 20, :order => :votes

post = Post.find(99)
post.popular_comments # lazy-load limited associations
Rails.cache.write('post', post)
...
Rails.cache.read('post').popular_comments # Unwanted SQL query :(
Run Code Online (Sandbox Code Playgroud)

我尝试过缓存克隆,同样不需要的SQL查询.我已经尝试使用redis和memcached实现,结果相同.奇怪的是,这个序列确实可以在控制台上运行,但在控制器或上面的视图中的简单用法失败(即SQL发生).

更新(2017年4月):我现在说这是一个愚蠢的前提.缓存整个对象通常是浪费的,因为它使用了大量的缓存存储,并且序列化/反序列化很慢.缓存关联(如本问题中所述)将该浪费乘以N.通常,只缓存原始ID和HTML片段会更有效.

ava*_*tok 6

尝试 post.popular_comments.reload

首先,当急切加载时,实际上忽略了限制.来自文档:

如果您急切地使用指定的:limit选项加载关联,它将被忽略,返回所有关联的对象

这意味着,就像您发现的那样,您必须自己强制关联到父对象.在我的实验中,post.popular_comments没有用(这是有道理的,因为它返回一个代理对象),有趣的是也没有post.popular_comments.all.post.popular_comments(true)然而,诀窍呢.在该代码下面调用reload,并且简单地执行post.popular_comments.reload也会将关联加载到父类中.

我不确定这两个中哪一个更正确,post.popular_comments(true)或者post.popular_comments.reload.两者看起来都有点脆弱,但第二个读得更好,更清楚地表达了你的意图.

我验证了这两种方法:

  1. 在memcache中存储了有限的关联
  2. 从缓存加载后没有触发SQL查询

我的脚本存储帖子:

require 'pp'
Rails.cache.clear
post = Post.first

#post.popular_comments(true)
post.popular_comments.reload

Rails.logger.info "writing to cache"
s = Rails.cache.write "post", post
Rails.logger.info "writing to cache done"
Run Code Online (Sandbox Code Playgroud)

并检索:

require 'pp'
Rails.logger.info "reading from cache"
post = Rails.cache.read "post"
Rails.logger.info "reading from cache done"
Rails.logger.info post.popular_comments.inspect
Run Code Online (Sandbox Code Playgroud)

如果我一个接一个地运行我的日志显示:

  Post Load (0.5ms)  SELECT `posts`.* FROM `posts` LIMIT 1
  Comment Load (0.5ms)  SELECT `comments`.* FROM `comments` WHERE `comments`.`post_id` = 1 ORDER BY votes LIMIT 20
writing to cache
writing to cache done
reading from cache
reading from cache done
[#<Comment id: 1, ...
Run Code Online (Sandbox Code Playgroud)

我的mySQL日志还确认第二个脚本不会触发关联查询.

这是通过Rails 3.1.1完成的