pej*_*ohn 4 memcached json caching ruby-on-rails dalli
我正在构建一个iPhone应用程序的Rails后端.
在分析我的应用程序之后,我发现以下调用在性能方面特别昂贵:
@messages.as_json
Run Code Online (Sandbox Code Playgroud)
此调用返回大约30个消息对象,每个消息对象包含许多子记录.如您所见,单个消息json响应可能会组成许多DB调用:
def as_json(options={})
super(:only => [...],
:include => {
:user => {...},
:checkin => {...}
}},
:likes => {:only => [...],
:include => { :user => {...] }}},
:comments => {:only => [...],
:include => { :user => {:only => [...] }}}
},
:methods => :top_highlight)
end
Run Code Online (Sandbox Code Playgroud)
平均来说,@messages.as_json呼叫(所有30个对象)需要近1100毫秒.
想要优化我已经使用了memcached.使用下面的解决方案,当我的所有消息对象都在缓存中时,平均响应现在是200-300ms.我对此很满意,但我遇到的问题是,这使缓存未命中的情况更加缓慢.如果缓存中没有任何内容,则现在需要超过2000毫秒进行计算.
# Note: @messages has the 30 message objects in it, but none of the child records have been grabbed
@messages.each_with_index do |m, i|
@messages[i] = Rails.cache.fetch("message/#{m.id}/#{m.updated_at.to_i}") do
m.as_json
end
end
Run Code Online (Sandbox Code Playgroud)
我知道检查每个对象的缓存必须有一些开销.但我猜测有一种比我现在的方式更有效的方法,这基本上是连续的,一个接一个.有关提高效率的指示吗?
我相信Rails.cache使用ActiveSupport::Cache::Store接口,它有一个read_multi确切的目的的方法.[1]
我想换出fetch了read_multi将提高你的表现,因为ActiveSupport::Cache::MemCacheStore有一个优化的实施read_multi.[2]
码
这是更新的实现:
keys = @messages.collect { |m| "message/#{m.id}/#{m.updated_at.to_i}" }
hits = Rails.cache.read_multi(*keys)
keys.each_with_index do |key, i|
if hits.include?(key)
@messages[i] = hits[key]
else
Rails.cache.write(key, @messages[i] = @messages[i].as_json)
end
end
Run Code Online (Sandbox Code Playgroud)
对于每次未命中,高速缓存写入仍然与高速缓存的一次往返同步执行.如果你想减少这种开销,可以看一下像workling一样异步运行后台代码.
请注意,启动异步作业的开销实际上要小于Rails.cache.write开始扩展体系结构之前的开销.
Memcached Multi-Set
看起来Memcached团队至少考虑过提供Multi-Set(批量写入)命令,但目前还没有任何ActiveSupport接口,并且不清楚实现提供了什么级别的支持.[3]