私は最近 7Signalsブログ の記事を読み、キャッシュキーを取得するのはどうしてなのか疑問に思いました。
オブジェクトのタイムスタンプを含むキャッシュキーがあれば十分です(つまり、オブジェクトを更新すると、キャッシュが無効になります)。しかし、キャッシュからフェッチしようとしているオブジェクトそのものにDBヒットを引き起こさずに、テンプレートでキャッシュキーをどのように使用しますか。
具体的には、たとえば、投稿のコメントをレンダリングする1対多の関係にどのように影響しますか。
Djangoでの例:
{% for comment in post.comments.all %}
{% cache comment.pk comment.modified %}
<p>{{ post.body }}</p>
{% endcache %}
{% endfor %}
Railsでのキャッシュは、たとえばmemcachedへのリクエストだけとは異なります(キャッシュキーを別のものに変換することは知っています)。キャッシュキーもキャッシュしますか?
既にロードされている単一のオブジェクトのストレートダンプをキャッシュする場合、はい、何も得られないか、またはほとんど得られません。それはそれらの例が説明しているものではありません-それらは階層を説明しているので、何かより低いものへの変更はまた、階層のより上のすべてへの更新をトリガーするはずです。
37signalsブログの最初の例では、_Project -> Todolist -> Todo
_を階層として使用しています。入力された例は次のようになります。
_Project: Foo (last_modified: 2014-05-10)
Todolist: Bar1 (last_modified: 2014-05-10)
Todo: Bang1 (last_modified: 2014-05-09)
Todo: Bang2 (last_modified: 2014-05-09)
Todolist: Bar2 (last_modified: 2014-04-01)
Todo: Bang3 (last_modified: 2014-04-01)
Todo: Bang4 (last_modified: 2014-04-01)
_
したがって、_Bang3
_が更新されたとしましょう。そのすべての親も更新されます:
_Project: Foo (last_modified: 2014-05-16)
Todolist: Bar2 (last_modified: 2014-05-16)
Todo: Bang3 (last_modified: 2014-05-16)
_
次に、レンダリングするときになると、データベースからのProject
のロードは基本的に避けられません。開始するにはポイントが必要です。ただし、その_last_modified
_はそのすべての子のインジケーターであるため、子をロードする前にキャッシュキーとして使用します。
ブログの投稿では個別のテンプレートを使用していますが、私はそれらを1つにまとめます。 1つの場所で完全な対話を確認すると、少しわかりやすくなります。
したがって、Djangoテンプレートは次のようになります。
_{% cache 9999 project project.cache_key %}
<h2>{{ project.name }}<h2>
<div>
{% for list in project.todolist.all %}
{% cache 9999 todolist list.cache_key %}
<ul>
{% for todo in list.todos.all %}
<li>{{ todo.body }}</li>
{% endfor %}
</ul>
{% endcache %}
{% endfor %}
</div>
{% endcache %}
_
_cache_key
_がまだキャッシュに存在するプロジェクトを渡すとします。すべての関連オブジェクトへの変更を親に伝達するため、その特定のキーがまだ存在するということは、レンダリングされたコンテンツ全体がキャッシュからプルされることを意味します。
その特定のプロジェクトが更新されたばかりである場合-たとえば、上記のFoo
のように-その子をレンダリングする必要があり、、その後のみそのプロジェクトのすべてのTodolistsに対してクエリを実行します。同様に、特定のTodolistの場合-そのリストのcache_keyが存在する場合、リスト内のTodoは変更されておらず、すべてをキャッシュから取得できます。
また、このテンプレートで_todo.cache_key
_を使用していないことにも注意してください。質問で言ったように、body
はすでにデータベースからプルされているので、価値はありません。ただし、何かをキャッシュする理由はデータベースのヒットだけではありません。たとえば、生のマークアップテキスト(StackExchangeの質問/回答ボックスに入力するものなど)を取得してHTMLに変換するには、結果をキャッシュするのに十分な時間がかかる場合があります。
その場合、テンプレートの内部ループは次のようになります。
_ {% for todo in list.todos.all %}
{% cache 9999 todo todo.cache_key %}
<li>{{ todo.body|expensive_markup_parser }}</li>
{% endcache %}
{% endfor %}
_
すべてをまとめるために、この回答の上部にある元のデータに戻りましょう。仮定すると:
Bang3
_が更新されましたexpensive_markup_parser
_を含む)をレンダリングしています次に、これはすべてがロードされる方法です:
Foo
はデータベースから取得されますFoo.cache_key
_(2014-05-16)はキャッシュに存在しませんFoo.todolists.all()
が照会されます:_Bar1
_および_Bar2
_がデータベースから取得されますBar1.cache_key
_(2014-05-10)すでにキャッシュに存在しています;それを取得して出力するBar2.cache_key
_(2014-05-16)はキャッシュに存在しませんBar2.todos.all()
が照会されます:_Bang3
_および_Bang4
_がデータベースから取得されますBang3.cache_key
_(2014-05-16)はキャッシュに存在しません{{ Bang3.body|expensive_markup_parser }}
_がレンダリングされますBang4.cache_key
_(2014-04-01)すでにキャッシュに存在しています;それを取得して出力するこの小さな例でのキャッシュによる節約は次のとおりです。
Bar1.todos.all()
expensive_markup_parser
_を3回回避:_Bang1
_、_Bang2
_、および_Bang4
_そしてもちろん、次に表示したときに_Foo.cache_key
_が見つかるので、レンダリングの唯一のコストは、データベースからFoo
だけを取得してキャッシュをクエリすることです。