ページ分割された結果をキャッシュし、更新時にパージします - 解決方法は?
-
01-07-2019 - |
質問
私はフォーラムを作成し、データベースの作業を軽減するために apc および memcache キャッシュ ソリューションを実装しています。
「Categories::getAll」のようなキーを使用してキャッシュ レイヤーの実装を開始しました。ユーザー固有のデータがある場合は、ユーザー ID などのキーを追加して、次の結果を取得します。 "User::getFavoriteThreads|1471"
. 。ユーザーが新しいお気に入りのスレッドを追加すると、キャッシュ キーを削除し、エントリを再作成します。
ただし、ここで問題が発生します。
フォーラムのスレッドをキャッシュしたいと思いました。非常に単純に、「Forum::getThreads|$iForumId」です。しかし...ページネーションを使用すると、これをいくつかのキャッシュ エントリに分割する必要があります。たとえば、
"Forum::getThreads|$iForumId|$iLimit|$iOffset".
誰かがフォーラムに新しいスレッドを投稿するまでは問題ありません。次は、以下のすべてのキーを削除する必要があります。 "Forum::getThreads|$iForumId"
, 制限やオフセットが何であっても。
この問題を解決する良い方法は何でしょうか?一致しないものが見つかるまで、考えられるすべての制限とオフセットをループすることは避けたいと思っています。
ありがとう。
解決
また、労力と CPU コストの観点から、キャッシュ データを保存するコストと、キャッシュによって得られる費用を比較してみることもできます。
フォーラムのビューの 80% がスレッドの最初のページを見ていることがわかった場合は、そのページのみをキャッシュすることを決定できます。つまり、キャッシュの読み取りと書き込みの実装がはるかに簡単になります。
ユーザーのお気に入りのスレッドのリストも同様です。これが各人がめったにアクセスしないものである場合、キャッシュによってパフォーマンスがあまり向上しない可能性があります。
他のヒント
ただのアップデート:データ使用に関する Josh の指摘は非常に良いものだと判断しました。人々はフォーラムの 50 ページを閲覧し続ける可能性は低いです。
このモデルに基づいて、各フォーラムの最新の 90 スレッドをキャッシュすることにしました。フェッチ関数では、制限とオフセットをチェックして、指定されたスレッドのスライスがキャッシュ内にあるかどうかを確認します。キャッシュ制限内であれば、array_slice() を使用して正しい部分を取得して返します。
こうすることで、フォーラムごとに 1 つのキャッシュ キーを使用できるようになり、キャッシュのクリア/更新にほとんど手間がかかりません :-)
また、他のリソースを大量に使用するクエリでは、フルンガブンガのモデルを使用してキー間の関係を保存したことも指摘しておきます。残念ながら、Stack Overflow では 2 つの回答を受け入れることができません。
ありがとう!
を拡張することでこれを解決できました memcache
グループとキー値のハッシュ テーブルを含む保護されたプロパティを持つカスタム クラス (ExtendedMemcache など) を持つクラス。
の ExtendedMemcache->set
メソッドは 3 つの引数を受け入れます ($strGroup
,$strKey
, $strValue
)セットを呼び出すと、間の関係が保存されます $strGroup
, 、 そして $strKey
, 、保護されたプロパティに保存し、 $strKey
に $strValue
の関係 memcache
.
その後、新しいメソッドを ExtendedMemcache
「deleteGroup」というクラス。文字列が渡されると、そのグループに関連付けられているキーを見つけて、各キーを順番に削除します。
それは次のようなものになります:http://pastebin.com/f566e913bすべてが理にかなっていて、うまくいくことを願っています。
PS.静的呼び出しを使用したい場合は、保護されたプロパティを次の場所に保存できると思います。 memcache
それ自体は独自のキーの下にあります。ちょっとした考え。
基本的にビューをキャッシュしようとしているのですが、これは常に困難になります。データはめったに変更されないため、代わりにデータのみをキャッシュするようにしてください。フォーラムをキャッシュするのではなく、スレッドの行をキャッシュします。その後、db 呼び出しはキャッシュに既にある ID のリストを返すだけです。db 呼び出しは、どの MyISAM テーブルでも超高速で行われるため、db メモリを消費する大規模な結合を行う必要がなくなります。
考えられる解決策の 1 つは、フォーラム内のスレッドのキャッシュをページ分割するのではなく、スレッド情報を Forum::getThreads|$iForumId
. 。次に、PHP コードで、そのページに必要なものだけを取り出します。
$page = 2;
$threads_per_page = 25;
$start_thread = $page * $threads_per_page;
// Pull threads from cache (assuming $cache class for memcache interface..)
$threads = $cache->get("Forum::getThreads|$iForumId");
// Only take the ones we need
for($i=$start_thread; $i<=$start_thread+$threads_per_page; $i++)
{
// Thread display logic here...
showThread($threads[$i]);
}
これは、各ページでキャッシュを取得するためにもう少し作業が必要ですが、新しいスレッドの更新または追加時に 1 か所のキャッシュを無効にすることだけを考慮する必要があることを意味します。
フルンガブンガ:あなたの解決策は私が探しているものに非常に近いです。これを妨げる唯一の点は、リクエストのたびに関係を memcache に保存し、ロードし直す必要があることです。
これがパフォーマンスにどの程度の影響を与えるかはわかりませんが、少し効率が悪いように思えます。いくつかのテストを行って、どのように機能するか見ていきます。構造化された提案をありがとうございます (そしてそれを示すためのコードもいくつかあります、ありがとう!)。
確実な事実を測定せずにこの種の最適化を実行する場合は、十分に注意してください。
ほとんどのデータベースには、いくつかのレベルのキャッシュがあります。これらが正しく調整されていれば、データベースはおそらく、ユーザー自身が行うよりもはるかに優れたキャッシュ処理を行うでしょう。
フルンガブンガへの返答:
グループ化を実装するもう 1 つの方法は、キー自体にグループ名とシーケンス番号を入力し、シーケンス番号をインクリメントしてグループを「クリア」することです。各グループの現在有効なシーケンス番号を独自のキーに保存します。
例えば
get seqno_mygroup
23
get mygroup23_mykey
<mykeydata...>
get mygroup23_mykey2
<mykey2data...>
次に、グループを単純に「削除」します。
incr seqno_mygroup
出来上がり:
get seqno_mygroup
24
get mygroup24_mykey
...empty
等..