シナリオ
Mutex は主に、データベースから memcache キャッシュに n 分間ロードされるホームページの
トップ 10 など、多数の同時アクセスとキャッシュの有効期限が存在する状況で使用されます
有名人のコンテンツ キャッシュWeibo、一度存在しないと、大量のリクエストはヒットしません そして、データベースをロードします
データベースを複数回クエリするなど、複数の IO 操作によって生成されたデータをキャッシュに保存する必要があります
問題
大規模な同時実行時状況によっては、キャッシュに障害が発生すると、多数の同時ユーザーが同時にキャッシュにアクセスできなくなり、データベースが同時にアクセスされることになります。また、キャッシュを元に戻すと、システムに潜在的な過負荷リスクが生じる可能性があります。当社のオンライン システムでも同様の障害が発生しました。
解決策
方法 1
データベースをロードする前にミューテックス キーを追加します。ミューテックス キーの追加が成功した後、追加が失敗した場合は、スリープして元のキャッシュ データの読み取りを再試行します。デッドロックを防ぐために、ミューテックス キーにも有効期限を設定する必要があります。疑似コードは次のとおりです
(注: 以下の疑似コードはアイデアを理解するためだけのものです。バグがある可能性があります。お気軽に指摘してください。)
Java コード
if (memcache.get(key) == null) { // 3 min timeout to avoid mutex holder crash if (memcache.add(key_mutex, 3 * 60 * 1000) == true) { value = db.get(key); memcache.set(key, value); memcache.delete(key_mutex); } else { sleep(50); retry(); } }
方法 2
Set a値内のタイムアウト値 (timeout1)、timeout1 は実際の memcache タイムアウト (timeout2) より小さくなります。 timeout1 がキャッシュから読み取られ、期限切れであることが判明すると、timeout1 は直ちに延長され、キャッシュにリセットされます。次に、データベースからデータをロードし、キャッシュに設定します。疑似コードは次のとおりです
Java コード
v = memcache.get(key); if (v == null) { if (memcache.add(key_mutex, 3 * 60 * 1000) == true) { value = db.get(key); memcache.set(key, value); memcache.delete(key_mutex); } else { sleep(50); retry(); } } else { if (v.timeout <= now()) { if (memcache.add(key_mutex, 3 * 60 * 1000) == true) { // extend the timeout for other threads v.timeout += 3 * 60 * 1000; memcache.set(key, v, KEY_TIMEOUT * 2); // load the latest value from db v = db.get(key); v.timeout = KEY_TIMEOUT; memcache.set(key, value, KEY_TIMEOUT * 2); memcache.delete(key_mutex); } else { sleep(50); retry(); } } }
オプション 1 との比較
利点: キャッシュがミューテックスの取得とスリープに失敗した場合に大量のリクエストを回避します
欠点: コードの複雑さが増加するため、オプション一般的には1で十分です。
オプション 2 は、Memcached FAQ にも詳しく紹介されています。更新の破壊、リクエストのスタンピングを防ぐ方法。また、ブラッドは、お気に入りのツールのもう 1 つである Gearman を使用して、キャッシュ設定の単一インスタンスを実装する方法も紹介しました。「キャッシュ」を参照してください。ミススタンピードだけどギアマンを使う ちょっとしたコツのような気がします。