キャッシュ無効化時

キャッシュの無効化は、おそらくプログラミングに関して最も混乱を招くものの1つです。 問題の微妙な点は、この手順の完全性、冗長性、複雑さの妥協点です。 それで、この記事は何についてですか? 障害システムを実装する方法を考えるために、プラットフォーム、言語、またはフレームワークに縛られたくはありません。 さて、すべてについて何も書かないために、今日よく見られるORMを使用して構築されたSQLクエリの結果のキャッシュに集中します。



完全性と冗長性


SQLクエリまたはORMに固有ではない一般的な考慮事項から始めましょう。 前述の完全性と冗長性を次のように定義します。 無効化の完全性はその特性であり、キャッシュにダーティデータが含まれる場合と発生する可能性のある頻度と状況を決定します。 冗長性は、キャッシュが不必要に無効化される頻度と呼ばれます。



たとえば、時間無効化の一般的な方法を考えてみましょう。 一方では、データを変更した直後にキャッシュがダーティになることをほぼ保証します。 一方、キャッシュがダーティのままである時間は、ライフタイムを減らすことで簡単に制限できます(これにより、ヒットの割合が減ります)。 つまり キャッシュの有効期間を短縮する一方で、無効化の完全性が向上し、冗長性が悪化します。 その結果、無効化の理想的な完全性(ダーティデータなし)を実現するには、タイムアウトを0に設定するか、つまりキャッシュを無効にする必要があります。 多くの場合、一時的なキャッシュの陳腐化は許容されます。 たとえば、原則として、数分後に最新ニュースのブロック内のニュースが表示されたり、ソーシャルネットワークのユーザーの総数が数千のエラーで示されたりしてもそれほど悪くはありません。



イベントの無効化


時間制限のある方法は、単純さの点では優れていますが、常に適用できるとは限りません。 さて、データが変更されたときにキャッシュをフラッシュできます。 このアプローチの問題の1つは、キャッシュする新しいリクエストを追加するときに、データを変更するときに無効にするコードを追加する必要があることです。 ORMを使用する場合、データを1つの場所で(良い場合には)変更します-モデルの保存中。 データを変更するための1つの中央コードが存在するとタスクが容易になりますが、多数のさまざまな要求があると、そこにキャッシュのさまざまな部分をダンプする新しい行を常に追加する必要があります。 したがって、過剰なコード接続が頭に浮かびます。 それを弱める時です。



イベントを使用します-ORMはモデルを保存/削除するときにイベントを生成し、何かをキャッシュするときは、キャッシュからそれを削除する対応するイベントでハンドラーをすぐにハングさせます。 すべては問題ありませんが、多数の同様のハンドラーを記述するのは面倒であり、加えてアプリケーションロジックは太った豚のようなキャッシング/無効化ロジックで大きくなりすぎています。



ORMリクエストの自動無効化


ORMがあることを思い出してください。ORMの場合、各リクエストはテキストだけでなく、モデル、条件ツリーなどの特定の構造を表します。 したがって、理論上、ORMは必要に応じてキャッシュするときに、無効なハンドラーを直接キャッシュおよびハングアップできます。 私のような怠け者にとっては魅力的なソリューションです。



小さな例。 リクエストを実行するとします:

select * from post where category_id =2 and published





それをキャッシュします。 明らかに、投稿を追加/更新/削除するときに、古いバージョンまたは新しいバージョンでcategory_id=2 and published=true



条件が満たされている場合、リクエストをリセットする必要がありcategory_id=2 and published=true



。 しばらくすると、モデルごとに無効なリストが形成され、各リストにはリセットする必要があるリクエストのリストが格納されます。

post:

category_id =2 and published = true :

select * from post where category_id =2 and published

select count ( * ) from post where category_id =2 and published

select * from post where category_id =2 and published limit 20

category_id =3 and published = true :

select * from post where category_id =3 and published limit 20 offset 20

category_id =3 and published = false :

select count ( * ) from post where category_id =3 and not published

foo:

a =1 or b =10 :

or_sql

a in ( 2 , 3 ) and b =10 :

in_sql

a >1 and b =10 :

gt_sql





など

実際には、ここではわかりやすくするために、クエリテキストではなく、無効化ツールにキャッシュキーのリストを保存する方が便利です。



オブジェクトが追加されたときに何が起こるか見てみましょう。 障害者のリスト全体を調べて、追加されたオブジェクトに対して満たされた条件のキャッシュキーを消去する必要があります。 しかし、多くの無効なものが存在する可能性があり、キャッシュ自体と同じ場所に保存する必要があります。 ほとんどの場合、プロセスの記憶にないため、毎回ダウンロードすることは望ましくありません。すべての条件を一貫してチェックすることは、借金にとって苦痛です。



明らかに、完全に検証せずに、何らかの形で障害のある人々をグループ化して排除する必要があります。 条件の値が異なる場合の図に注意してください。 たとえば、投稿モデルの障害者はすべて、category_id =? およびpublished =?..スキームに従って、障害者を例からグループ化します。

post:

category_id =? and published =? :

2 , true :

select * from post where category_id =2 and published

select count ( * ) from post where category_id =2 and published

select * from post where category_id =2 and published limit 20

3 , true :

select * from post where category_id =3 and published limit 20 offset 20

3 , false :

select count ( * ) from post where category_id =3 and not published

foo:

a =? or b =? :

1 , 10 :

or_sql

a in ? and b =? :

( 2 , 3 ), 10 :

in_sql

a > ? and b =? :

1 , 10 :

gt_sql







条件category_id =?に注意してください そして、公開された= ?、追加された投稿のフィールドの値を知っているので、ラベル「?」を明確に記入できます。 オブジェクトの場合:

{id : 42 , title : "…" , content : "…" , category_id : 2 , published : true }





、ファミリーから適切な無効なものはcategory_id = 2およびpublished = trueのみであるため、それに対応する3つのキャッシュキーを消去する必要があります。 つまり 条件の一貫したチェックは不要であり、オブジェクトのスキームとデータに従って必要な無効をすぐに受け取ります。



しかし、より困難な状況ではどうすればよいでしょうか? 場合によっては、何かを行うことができます。または、またはへのデプロイ時に、2つの無効化ツールに分割します。 その他の場合、事態を複雑にするか、無効化を冗長にして、そのような条件を破棄する必要があります。 これらの変換後のfooの障害は次のとおりです。

foo:

a = ? :

1 : or_sql

b = ? :

10 : or_sql, gt_sql

a = ? and b = ? :

2 , 10 : in_sql

3 , 10 : in_sql







したがって、モデルごとにスキーム(フィールドのリスト)を保存するだけでよく、必要に応じて、無効にするキーを作成し、消去するキーのリストを要求します。



fooの無効化手順の例を示します。 データベースからオブジェクト{id : 42 , a : 1 , b : 10 }





をリクエストしたと仮定します {id : 42 , a : 1 , b : 10 }





aの値を2に変更して書き戻しました。 更新時には、オブジェクトの古い状態と新しい状態の両方に対して無効化手順を実行する必要があります。 したがって、古い状態の無効化要素: a =1 , b =10 , a =1 and b =10



、対応するキーor_sqlおよびgt_sql(最後の無効化要素は存在せず、空と見なすことができます)。 新しい状態の場合、無効化子a =2 , b =10 , a =2 and b =10



取得し、in_sqlキーを追加します。 その結果、3つの要求はすべて消去されます。



実装


言語とプラットフォームから可能な限り自分を抽象化しようとしましたが、かなりロードされたプロジェクトの作業システムと作業システムも存在します。 次の記事で、それに関する詳細と実装のコツについて説明します。



All Articles