2

私はMemcachedを介してRailsプロジェクトにキャッシュを実装しており、特にサイドカラムブロック(最新の写真、ブログなど)をキャッシュしようとしていますが、現在、15分程度ごとにキャッシュが期限切れになっています。これは機能しますが、新しいコンテンツが追加されたり、更新されたりするたびに、より最新の状態にすることができれば、それはより良いことです。

Memcachedhttp : //content.newrelic.com/railslab/videos/08-ScalingRails-Memcached-fixed.mp4でScalingRailsスクリーンキャストのエピソードを見ていました。ビデオの8:27に、GreggPollackがインテリジェントについて話します。インテリジェントキー(この例ではupdated_atタイムスタンプ)を使用して、キャッシュを期限切れにすることなく以前にキャッシュされたアイテムを置き換える方法でMemcachedにキャッシュします。したがって、タイムスタンプが更新されるたびに、新しいタイムスタンプを探すときにキャッシュが更新されると思います。

この例では「最近の写真」サイドブロックを使用していますが、これが設定方法です...

_side-column.html.erb:

<div id="photos"">
   <p class="header">Photos</p>
   <%= render :partial => 'shared/photos', :collection => @recent_photos %>     
</div>

_photos.html.erb

<% cache(photos) do %>
  <div class="row">
    <%= image_tag photos.thumbnail.url(:thumb) %>
    <h3><%= link_to photos.title, photos %></h3>
    <p><%= photos.photos_count %> Photos</p>
  </div>
</div>

<%終了%>

最初の実行時に、Memcachedはブロックをviews / photos / 1-20110308040600としてキャッシュし、ページが更新されたときにそのキャッシュされたフラグメントをリロードします。次に、バックエンドのその特定の行に写真を追加してリロードしますが、写真の数は更新されません。ログは、views / photos / 1-20110308040600からまだロード中であり、更新されたタイムスタンプを取得していないことを示しています。私がしていることはすべて、ビデオがしていることと同じように見えますが、上記で何が間違っているのですか?

さらに、この質問にはパート2があります。上記の部分でわかるように、コレクションに対して@recent_photosクエリが呼び出されます(私のlibフォルダー内のモジュールから)。ただし、ブロックがキャッシュされている場合でも、このSELECTクエリが呼び出されていることに気付きました。<%cache(@recent_photos)do%>のように、最初は部分全体をブロックにラップしようとしましたが、明らかにこれは機能しません-特にコレクション全体に実際のタイムスタンプがないため、もちろん個々のアイテムです。結果がすでにキャッシュされている場合、どうすればこのクエリが実行されないようにできますか?

更新 2番目の質問を参照すると、Rails.cache.existがない限り、それがわかりました。私のチケットかもしれませんが、注意が必要なのは、タイムスタンプを使用するワイルドカードの性質です...

UPDATE 2 最初の質問を完全に無視して、キャッシュが更新されなかった理由を正確に理解しました。これは、updated_atフィールドが更新されていなかったためです。その理由は、親のネストされたリソースであるアイテムを追加/削除していたためです。親のupdated_atフィールドを更新するには、おそらくそのアイテムに「タッチ」を実装する必要があります。

しかし、私の2番目の質問はまだ残っています...フラグメントがキャッシュされている場合でも、メインの@recent_photosクエリがまだ呼び出されています... cache.existsを使用する方法はありますか?/ views / photos / 1-2011randomのような名前のキャッシュをターゲットにするには?

4

2 に答える 2

2

Railsキャッシングの主な欠点の1つは、キャッシュされたコンポーネントのコントローラーとビューを確実に分離できないことです。私が見つけた唯一の解決策は、キャッシュされたブロックにクエリを直接埋め込むことですが、ヘルパーメソッドを使用することをお勧めします。

たとえば、おそらく次のようなものがあります。

class PhotosController < ApplicationController
  def index
    # ...

    @recent_photos = Photos.where(...).all

    # ...
  end
end

最初の本能は、キャッシュされたコンテンツの存在のテストなど、ビューで必要になる場合にのみそのクエリを実行することです。残念ながら、コンテンツがキャッシュされていることをテストしてから実際にページをレンダリングするまでの間にコンテンツが期限切れになる可能性がわずかにあります。これにより、nil-value@recent_photosを使用するとテンプレートのレンダリングエラーが発生します。

より簡単なアプローチは次のとおりです。

<%= render :partial => 'shared/photos', :collection => recent_photos %>

インスタンス変数を使用する代わりに、ヘルパーメソッドを使用してください。コントローラ内に負荷をかけるのと同じように、ヘルパーメソッドを定義します。

module PhotosHelper
  def recent_photos 
    @recent_photos ||= Photos.where(...).all
  end
end

この場合、値が保存されるため、同じヘルパーメソッドを複数回呼び出すと、クエリが1回だけトリガーされます。これはアプリケーションでは必要ない場合があり、省略できます。結局のところ、この方法で義務付けられているのは、「最近の写真」のリストを返すことだけです。

Railsが独自の関連ビューを持つサブコントローラーをサポートしていれば、この混乱の多くを解消できます。これは、ここで採用されているパターンのバリエーションです。

于 2011-04-08T20:04:06.417 に答える
1

この質問をして以来、キャッシングをさらに進めてきたので、この種のキャッシング手法の価値を正確に理解し始めていると思います。

たとえば、私は記事を持っていて、他のテーブルへのクエリを含むページに必要なさまざまなことを通して、記事ごとに5〜7つの異なるクエリを実行する必要があるかもしれません。ただし、この方法で記事をキャッシュすると、これらすべてのクエリが1つに減ります。

この手法では、タイムスタンプが更新されたかどうかを判断する「何らかの」方法が必要であるため、常に少なくとも「1つの」クエリが必要であると想定しています。

于 2011-04-30T17:54:09.587 に答える