これらの投稿では、memcached、そのアーキテクチャ、可能なアプリケーション、キャッシュキーの選択、クラスタリング、アトミック操作、 memcachedのカウンターの実装、およびキャッシュを同時に再構築する問題について説明しました。
今日は、キャッシュのタグ付けと、memcachedのキャッシュグループをすぐに削除する可能性について説明します。
最後の6番目の投稿では、memcachedを使用する際のさまざまな技術的な問題、つまり統計の分析、デバッグなどについて説明します。
フラッシュキャッシュグループ
たとえば、データベースからの選択など、バックエンドから一部のデータをキャッシュすると、遅かれ早かれ、元のデータが変更され、キャッシュは無効になります。 さらに、変更後すぐにキャッシュをフラッシュすることが非常に望ましいです。そうしないと、編集後のユーザーはオブジェクトの古いバージョンを見ることができ、間違いなく混乱します。 この状況の単純なバージョンがあります。ID35のオブジェクトに関する情報を変更し、パラメーターID = 35によってこのオブジェクトの選択のキャッシュをリセットします。 実際には、ほとんどの場合、同じオブジェクトが多数のサンプルに明示的または暗黙的に含まれているため、キャッシュされます。
この例を考えてみましょう。ブログホスティングを作成しました。多数のブログがあります。 作成者の1人が新しい投稿を作成すると、多数の選択が変更されます。投稿リストのメインページとすべての2番目のページの投稿(すべての投稿が1つ移動したため)、投稿カレンダーのエントリ数が変更され、RSSが変更され、など もちろん、これらのサンプルのキャッシュを短いライフタイムで提供し、しばらくするとリセットされて正しい情報が表示されますが、キャッシュ時間が短すぎると(たとえば5秒)、キャッシュに対するヒット率が低くなり、負荷が増加しますデータベースと長いデータベースは、投稿の作成後に情報が更新されていない、つまり投稿が追加されていないという感覚をユーザーに与えます。 同時に、ブログホスティングのフレームワークでは、このブログに関連付けられているすべてのキャッシュを削除しても、これはキャッシュの総量のごくわずかな割合にすぎないことに気付くことができます(多くのブログがあるため)。 問題は残っています。このブログのすべてのキャッシュを見つけて特定する方法は? それらのいくつかを簡単に構築できますが、既に不便になるものもあります。たとえば、投稿のページネーションリストのキャッシュの数は、まだ計算する必要があるページの数に依存します。 どうする?
考えられる解決策の1つは、タグ付けキャッシュです。 以下で説明するタグ付け方法は、Dmitry Koterovのメモで説明されているものと本質的に同じですが、独自に開発しました。 他のタグ付けオプション、たとえばmemcachedのmemcached-tagパッチがあります。
キャッシュタグ
そこで、新しい概念-キャッシュタグを導入します。 キャッシュはタグのリストに関連付けられています。 タグ自体は、名前とそれに関連付けられたバージョン(番号)です。 タグバージョンは単調にしか成長できません。 キャッシュのグループは、1つの共通タグを持つキャッシュと呼ばれます。 キャッシュのグループをリセットするには、対応するタグのバージョンを増やすだけで十分です。
ソフトウェアレベルでは、この選択をキャッシュする必要があり、そのキャッシュはタグ
tag1
と
tag1
関連付けられることがわかっています(この事実はアプリケーションのロジックによって決定されます)。 キャッシュを作成する場合、キャッシュされたデータに加えて、タグ
tag1
および
tag1
バージョン(キャッシュ作成時)を書き込みます。 キャッシュを受信すると、有効期限が切れておらず、タグ
tag1
および
tag1
現在のバージョンがキャッシュに記録されているバージョンと等しい場合、キャッシュは有効であると見なされます。 したがって、
tag1
タグの
tag1
を変更(増加)
tag1
、以前に作成されたこのタグに関連付けられているすべてのキャッシュは無効になります(
tag1
タグの小さいバージョンが書き込まれているため)。
サンプルを使用した例を考えてみましょう。次のようにします。
: tag1 -> 25 tag2 -> 63 : [ : 2008-11-07 21:00 : [ … ] : [ tag1: 25 tag2: 63 ] ]
その後、何らかのイベントが発生し、
tag2
タグに関連付けられたすべてのキャッシュを
tag2
することにしました。 タグのバージョンを増やしました:
tag2++
。 タグのバージョンが変更されました:
: tag1 -> 25 tag2 -> 64
キャッシュの有効期限がまだ切れていないにもかかわらず、キャッシュは有効ではなくなりました。キャッシュに保存されているtag2タグのバージョン(63)は現在のバージョン(64)と一致しません。
タグのバージョン
タグ(つまり、そのバージョン)をキャッシュを保存する場所、つまりmemcachedに保存するのは理にかなっています。 タグごとに、タグ名と一致する名前のキーを作成します。その値はタグのバージョンになります。 タグバージョンとして何を使用するかを決めるのはまだですか? タグのバージョンを変更するときに数字だけを増やして数字を使用することもできますが、これにより、キーが失われる可能性がある場合に誤った動作を引き起こす可能性があります。 タグのバージョンを1に設定し、このタグで選択範囲をキャッシュし、タグの値(キャッシュに1つ)を書き込みます。 その後、タグバージョンのキーがmemcachedから削除され、次の瞬間にタグに関連付けられた選択をリセットしたい、つまりタグバージョンを増やす必要がありました。 タグバージョンの値を失ったため、ユニットを再び1に設定し、キャッシュは有効であると見なされますが、キャッシュはリセットされています(タグバージョンを増やすときに選択する値は関係ありません。以前)。
現在の時刻をバージョンとして使用する方が便利です(たとえば、ミリ秒まで十分な精度で)。 次に、タグのバージョンを増やすと、以前のバージョンが失われた場合でも、常に新しい、より大きなバージョンが生成されます。 タグバージョンはフロントエンドで生成されるため、システムクロックを同期する必要があります(これがない場合、たとえば、有効期間が短いキャッシュの有効期限を正しく計算するなど、他の機能を使用します)。このバージョン計算方法の選択に問題はありません
現在の時刻をタグのバージョンとして使用すると、プロジェクトデータベースがマスタースレーブレプリケーションスキームに従って配置されている状況で、別の利点が得られます。 データベース内のソースオブジェクトを変更する場合、それに関連付けられているタグのバージョンを変更します(そこに現在の時刻、つまり変更時刻を書き込みます)。 別のプロセスでは、キャッシュが古くなっていることがわかります。つまり、キャッシュを再構築する必要があります。再構築は読み取り要求(
SELECT
)であり、データベーススレーブサーバーに送信する必要がありますが、レプリケーションの遅延により、スレーブサーバーはオブジェクトの実際のバージョンをまだ受信できないその結果、DBはキャッシュを削除しましたが、それを再構築するときに、オブジェクトの古いバージョンを再度キャッシュしましたが、これは受け入れられません。 どのデータベースサーバーにリクエストを送信するかを決定するときにタグバージョンを使用できます:現在の時刻とキャッシュタグのバージョンの差が、最大レプリケーション遅延によって決定される特定の間隔より小さい場合、スレーブではなくデータベースマスターサーバーにリクエストを送信します。
このようなタグスキームを使用すると、memcachedへのリクエストの数が増えます。
キャッシュごとにタグのバージョンを取得する必要があります。 オーバーヘッドコストは、複数取得memcachedリクエストを使用すること、および同じプロセス内でmemcachedキーをローカルにキャッシュすることにより削減できます(同じタグが複数のキャッシュに関連付けられている場合)。