Dklab_Cacheライブラリ
Dklab_Cacheは、Zend Frameworkインターフェイスを使用する(ほとんど)memcachedキータグ付けサポートライブラリです。 ライブラリ自体は純粋なPHPで書かれています。 ライブラリ機能の完全なリストは次のとおりです。
- Backend_TagEmuWrapper:memcachedおよびその他のZend Frameworkバックエンドキャッシングシステムのタグ。
- Backend_NamespaceWrapper:memcachedなどの名前空間のサポート。
- Backend_Profiler:memcachedおよびその他のバックエンドの使用に関する統計。
- Frontend_Slot、Frontent_Tag:複雑なプロジェクトでキャッシュシステムを高レベルで構築するためのフレームワーク。
TagEmuWrapperは、標準のZend_Cache_Backend_Interfaceバックエンドインターフェイスを実装しているため、呼び出し側の観点から見ると、それ自体がキャッシュバックエンドです。 一般に、Zend Frameworkは、インターフェイスレベルで最初からタグをサポートしているため、優れています。 たとえば、save()メソッドにはすでにキーをタグ付けできるパラメーターがあります。 ただし、Zend Frameworkのバックエンドはいずれもタグをサポートしていません。特定のキーにタグを追加しようとすると、例外が発生します(特にZend_Cache_Backend_Memcachedの場合)。
技術的な詳細、ドキュメント、および使用例は、 dklab.ru / lib / Dklab_Cacheにあります。
タグとは何ですか?
典型的なキャッシングシステム(memcachedを含む)の操作は、3つの主要な操作で構成されています。
- save($ data、$ id、$ lifetime):キャッシュセルにデータ$ dataをキー$ idで保存します。 キー$ lifetimeの「lifetime」を指定できます。 この時間が経過すると、キャッシュ内のデータは「不良」になり、削除されます。
- load($ id):キー$ idのセルからデータをロードします。 データが利用できない場合、falseが返されます。
- remove($ id):キーidのキャッシュセルをクリアします。
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}」という行はまさに知識であり、この知識を不必要に多数の独立した場所に広めることの害を過小評価すべきではありません。 この例では、知識は非常に単純ですが、実際には、キャッシュキーは数十の異なるパラメーターに依存する場合があり、その一部は要求に応じてデータベースからロードする必要さえあります。
状況は実際には見かけよりもさらに深刻です。
- 現在のユーザーIDが$ userId変数に保存され、ゴミではないことを誰が保証できますか? しかし、誰かが間違ったデータを代用しようとしたらどうでしょうか? 明らかに、キャッシュキーは実際にはユーザーIDに依存するのではなく、そのユーザーに依存します。 キーを生成するためにユーザーオブジェクト以外を使用する試みは明らかに誤りですが、この制限はプログラムで明示的に表現されていません。
- コードに直接触れることなく変更できるように、キャッシュ時間をコードに直接保存するのではなく、システム構成のどこかに保存する必要があります(前の例を参照)。 これは、キャッシュセルとプロファイルラインの役割への別の依存関係です。
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_UserProfileクラスに含まれています。
- また、キャッシュの有効期間に関する知識も含まれています。 この場合、明示的に設定しますが、構成パラメーターから有効期間を取得することを誰も気にせず、その名前はスロットクラスの名前と一致します。
- Cache_Slot_UserProfileクラスのコンストラクターのパラメーター$ userが入力されます。 これは、正しいユーザーオブジェクト以外のものをスロットクラスに「パームオフ」できないことを意味します。 当然、依存関係はいくつかのオブジェクトに依存します。 これらはすべて、コンストラクターのパラメーターによって決定されます。
さて、実際には、タグについて
スロットは、とりわけ、タグ付けをサポートしています。 エンドツーエンドのキャッシングにタグを使用する例を次に示します(もちろん、「パススルー」を使用できます)。
$ 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