Rails&Memcached:优化多个提取

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)

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

Seb*_*man 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
Run Code Online (Sandbox Code Playgroud)

对于每次未命中,高速缓存写入仍然与高速缓存的一次往返同步执行.如果你想减少这种开销,可以看一下像workling一样异步运行后台代码.

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

Memcached Multi-Set

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