4

我正在为 iPhone 应用程序构建 Rails 后端。

在分析我的应用程序后,我发现以下调用在性能方面特别昂贵:

@messages.as_json

此调用返回大约 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

平均而言,@messages.as_json调用(所有 30 个对象)需要将近 1100 毫秒。

想要优化我已经使用了 memcached。使用下面的解决方案,当我所有的消息对象都在缓存中时,平均响应现在是 200-300 毫秒。我对此很满意,但我遇到的问题是这使得缓存未命中情况变得更慢。在缓存中没有任何内容的情况下,现在需要超过 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

我知道检查每个对象的缓存必须有一些开销。但我猜有一种比我现在的方式更有效的方法,基本上是连续的,一个接一个的。关于提高效率的任何指示?

4

2 回答 2

8

我相信Rails.cache使用该ActiveSupport::Cache::Store接口,该接口具有read_multi用于此确切目的的方法。[1]

我认为换掉fetchread_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

高速缓存写入仍然与每次未命中的高速缓存往返同步执行。如果您想减少这种开销,请查看使用诸如workling 之类的异步运行后台代码。

请注意,启动异步作业的开销实际上小于Rails.cache.write开始扩展架构之前的开销。

Memcached 多集

看起来 Memcached 团队至少考虑过提供 Multi-Set(批量写入)命令,但目前还没有任何 ActiveSupport 接口,也不清楚实现提供了何种级别的支持。[3]

于 2013-01-17T23:50:29.970 回答
3

从 Rails 4.1 开始,您现在可以执行 fetch_multi 并传入一个块。

http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html#method-i-fetch_multi

keys = @messages.collect { |m| "message/#{m.id}/#{m.updated_at.to_i}" }
hits = Rails.cache.fetch_multi(*keys) do |key|
  @messages[i] = @messages[i].as_json
end

注意:如果您要设置许多项目,您可能需要考虑在某种后台工作人员中写入缓存。

于 2014-10-10T17:39:29.530 回答