Dklab_Cache:memcachedのタグ、名前空間、統計

Memcachedコミュニティは、memcachedコードの「ネイティブ」パッチを作成するために多くの試みを行い、タグサポートを追加しました。 これらのパッチの中で最も有名なのは、memcached-tagプロジェクトです。 残念ながら、memcached-tagはまだ安定版とはかけ離れています。パッチを適用したmemcachedサーバーをフリーズさせるスクリプトを書くことは難しくありません。 この記事を書いている時点では、memcachedサーバー自体のレベルでのタグ付けの問題に対する信頼できる解決策はないようです。



Dklab_Cacheライブラリ



Dklab_Cacheは、Zend Frameworkインターフェイスを使用する(ほとんど)memcachedキータグ付けサポートライブラリです。 ライブラリ自体は純粋なPHPで書かれています。 ライブラリ機能の完全なリストは次のとおりです。

実際、タグをサポートするために、TagEmuWrapperクラスがあります。 これは、Zend Frameworkバックエンドキャッシングクラスのデコレーター(「ラッパー」)です。 言い換えれば、Zend Frameworkのキャッシングサブシステムに「透過的に」タグサポートを追加するために使用できます。 memcached:Zend_Cache_Backend_Memcachedを操作するためのバックエンドを検討しますが、プロジェクトで他のバックエンドクラスを使用している場合は、特別な機能なしでタグ付けを接続できます。



TagEmuWrapperは、標準のZend_Cache_Backend_Interfaceバックエンドインターフェイスを実装しているため、呼び出し側の観点から見ると、それ自体がキャッシュバックエンドです。 一般に、Zend Frameworkは、インターフェイスレベルで最初からタグをサポートしているため、優れています。 たとえば、save()メソッドにはすでにキーをタグ付けできるパラメーターがあります。 ただし、Zend Frameworkのバックエンドはいずれもタグをサポートしていません。特定のキーにタグを追加しようとすると、例外が発生します(特にZend_Cache_Backend_Memcachedの場合)。



技術的な詳細、ドキュメント、および使用例は、 dklab.ru / lib / Dklab_Cacheにあります。



タグとは何ですか?



典型的なキャッシングシステム(memcachedを含む)の操作は、3つの主要な操作で構成されています。

ページの一部をすばやく表示するために長いSQLクエリをキャッシュするとします。 この場合、このリクエストに対応するキャッシュセルにエントリがあるかどうかを確認します。 セルが空の場合、データはDBMSからダウンロードされ、将来の検索のためにキャッシュに保存されます。



 if(false ===($ data = $ cache-> load( "key"))){
     $ data = executeHeavyQuery();
     $ cache-> save($ data、 "key");
 }
表示($データ);


残念ながら、純粋な形では、このアプローチはそれほど頻繁には適用できません。 実際、データベース内のデータは変更される可能性があるため、キャッシュセルを何らかの方法でクリアして、ユーザーがこれらの変更の結果をすぐに確認できるようにする必要があります。 キーでremove()メソッドを使用できますが、多くの場合、データを更新する時点では、キャッシュされているセルはわかりません。



実際、問題はもっと複雑です。 負荷の高いシステムでは、データは1秒間に数百回テーブルに追加されます。 したがって、依存関係を追跡し、どのキャッシュセルをクリアする必要があるかをチェックするロジックは、非常に複雑になります(または完全に不可能にさえなります)。



タグ付けは、この問題の解決策を提供します。 キャッシュセルにデータが書き込まれるたびに、タグ(システムの他の部分へのこのデータの依存関係を表すマーク)でマークを付けます。 タグを使用すると、セルを複数の交差するグループに結合できます。 将来的には、「特定のタグでマークされたすべてのセルをクリアする」コマンドを提供できます。



タグを使用して前の例を変更しましょう。 SQLクエリが現在のユーザー$ loggerUserIdのIDに大きく依存しているため、各ユーザーに「key _ {$ loggedUserId}」という名前の個別のセルが割り当てられているとします。 ただし、データは、現在のユーザーが表示しているプロファイルである別の人$ ownerUserIdのIDにも依存します。 この場合、ユーザー$ ownerUserIdに関連付けられたタグでセルをマークできます。



 if(false ===($ data = $ cache-> load( "key _ {$ loggedUserId}"))){
     $ data = loadProfileFor($ loggedUserId、$ ownerUserId);
     $ cache-> save($ data、 "key _ {$ loggedUserId}"、array( "profile _ {$ ownerUserId}");
 }
表示($データ);


ここで、$ ownerUserIdユーザープロファイルのデータが変更された場合(たとえば、ユーザーが名前を変更した場合)、このプロファイルに関連付けられたタグをクリアするコマンドを与える必要があります。



 $ cache-> clean(Zend_Cache :: CLEANING_MODE_MATCHING_TAG、array( "profile _ {$ ownerUserId}");


他のすべてのユーザーのキャッシュセルは影響を受けないことに注意してください。$ ownerUserIdに依存するもののみがクリアされます。



実際、「セルCにタグTを付ける」というフレーズは、「セルCはTとして記述されたデータに依存する」というステートメントと同じ意味です。 タグは依存関係であり、それ以上のものではありません。



小さな余談:コードの依存関係について



タグについての話を続ける前に、少し戻ってより一般的な概念、つまり依存関係について話しましょう。 これらの中毒とは何ですか? 一般的な場合(タグを使用しない場合でも)、データを効果的に使用するには、キャッシュキーを数回参照する必要があります。



 if(false ===($ data = $ cache-> load( "profile _ {$ userId}"))){
     $ data = loadProfileOf($ userId);
     $ cache-> save($ data、 "profile _ {$ userId}"、array()、3600 * 24);  // 24時間のキャッシュ
 }
表示($データ);


そして、プログラムのまったく異なる部分で:



 $ cache-> remove( "profile _ {$ userId}");


ご覧のとおり、「profile _ {$ userId}」というフレーズを3回まで繰り返す必要があります。 そして、最初のケースで、新しい変数の導入を犠牲にして繰り返しを削除できる場合:



 $ cacheKey = "profile _ {$ userId}";
 $ cacheTime = Config :: getInstance()-> cacheTime-> profile;
 if(false ===($ data = $ cache-> load($ cacheKey))){
     $ data = loadProfileFor($ userId);
     $ cache-> save($ data、$ cacheKey、array()、$ cacheTime);
 }
表示($データ);


...次に、プログラムの2番目の部分では、キャッシュキーがどのように構築され、どのキーが依存するかについての知識を取り除くことができません。



重要なお知らせ

「profile _ {$ userId}」という行はまさに知識であり、この知識を不必要に多数の独立した場所に広めることの害を過小評価すべきではありません。 この例では、知識は非常に単純ですが、実際には、キャッシュキーは数十の異なるパラメーターに依存する場合があり、その一部は要求に応じてデータベースからロードする必要さえあります。



状況は実際には見かけよりもさらに深刻です。

Dklab_Cacheでの仕組み



長い説明の代わりに、Dklab_Cache_Frontendのイデオロギーに従って構築されたSlotクラスを使用する例をすぐに示します。



 $ slot = new Cache_Slot_UserProfile($ user);
 if(false ===($ data = $ slot-> load())){
     $ data = $ user-> loadProfile();
     $ slot-> save($ data);
 }
表示($データ);


キャッシュをクリアするには:



 $ slot = new Cache_Slot_UserProfile($ user);
 $ slot-> remove();


何が良いですか?

プログラム内のキャッシュストレージのタイプと同数のカスタムスロットクラスを記述する必要があります。 この分野:Cache / Slotディレクトリを調べると、プログラムで使用されているさまざまなキャッシュの数とそれらが依存しているものをすぐに確認できます。



さて、実際には、タグについて



スロットは、とりわけ、タグ付けをサポートしています。 エンドツーエンドのキャッシングにタグを使用する例を次に示します(もちろん、「パススルー」を使用できます)。



 $ slot = new Cache_Slot_UserProfile($ user);
 $ slot-> addTag(新しいCache_Tag_User($ loggedUser);
 $ slot-> addTag(新しいCache_Tag_Language($ currentLanguage);
 $ data = $ slot-> thru($ user)-> loadProfile();
表示($データ);


システムにはさまざまな種類の依存関係があるのと同じ数のタグクラスを作成する必要があります。 タグクラスは、いくつかのタグをクリアするときに特に便利です。



 $ tag = new Cache_Tag_Language($ currentLanguage);
 $ tag-> clean();


ご覧のとおり、タグの依存関係に関する知識は1つの場所に保存されます。 これで、誤って間違ったタグを「ミス」してクリアすることができなくなります。システムは、存在しないクラスまたはコンストラクターパラメーターの間違ったタイプに関するエラーを表示します。



おわりに



この記事では、キャッシュのタグ付け、コード内のキャッシュ依存関係、ライブラリに実装されたスロットおよびタグキャッシュストレージからの抽象化方法の両方について、すべてを一度に説明します。



ライブラリのソースとサンプルはこちらからダウンロードできます: dklab.ru/lib/Dklab_Cache



All Articles