4

Rails バックエンドを iPhone アプリに構築しています。

アプリケーションをプロファイリングした後、次の呼び出しがパフォーマンスの点で特に高価であることがわかりました。

@messages.as_json

この呼び出しは、それぞれが多くの子レコードを含む約 30 個のメッセージ オブジェクトを返します。ご覧のとおり、1 つのメッセージ 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使用していると思います。[1]ActiveSupport::Cache::Storeread_multi

の実装が最適化されているため、 を交換するfetchread_multiパフォーマンスが向上すると思います。[2]ActiveSupport::Cache::MemCacheStoreread_multi

コード

更新された実装は次のとおりです。

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

キャッシュへの書き込みは、ミスごとにキャッシュへの 1 回の往復で同期的に実行されます。そのオーバーヘッドを削減したい場合は、worklingなどでバックグラウンド コードを非同期に実行することを検討してください。

Rails.cache.write非同期ジョブを開始するオーバーヘッドは、アーキテクチャの拡張を開始する前のオーバーヘッドよりも実際には少ないことに注意してください。

Memcached マルチセット

Memcached チームは、少なくともマルチセット (バッチ書き込み) コマンドの提供を検討しているようですが、そのための 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 に答える