厳密に言えば、強整合性を達成するために読み取りと書き込みのブロッキングが使用されない限り、非アトミックな操作では整合性を保証できません。したがって、キャッシュ アーキテクチャで追求する目標は結果整合性です。
キャッシュは、強整合性を犠牲にすることでパフォーマンスを向上させます。
これは CAP 理論によって決定されます。キャッシュ システムに適用できるシナリオは、CAP 内の AP に属する非強整合性シナリオです。
次の 3 つのキャッシュ読み取りおよび書き込み戦略にはそれぞれ長所と短所があり、最適な戦略はありません。
キャッシュ アサイド パターン、つまりバイパス キャッシュ モードは、次の課題を解決するために提案されています。キャッシュとデータベース間のデータの不整合の問題を可能な限り解決します。
Read: キャッシュからデータを読み取り、読み取り後に直接返します。読み取れない場合は、データベースからロードしてキャッシュに書き込み、応答を返します。
書き込み: 更新する場合は、まずデータベースを更新し、次にキャッシュを削除します。
Write: まずキャッシュを確認し、キャッシュに存在しない場合は直接DBを更新します。キャッシュ内に存在する場合は、まずキャッシュが更新され、その後キャッシュサービスが自らDBを更新します(キャッシュとDBを同時に更新)。
Read: キャッシュからデータを読み取り、読み取り後に直接返します。読み取れない場合はDBから読み込み、キャッシュに書き込んでからレスポンスを返します。
ライト ビハインド パターン (非同期キャッシュ書き込み)ライト ビハインド パターンは、読み取り/書き込みスルー パターンと非常によく似ており、どちらもキャッシュ サービスによって処理され、キャッシュと DB の読み取りと書き込みを行います。 ただし、この 2 つには大きな違いがあります。Read/Write Through はキャッシュと DB を同期的に更新しますが、Write Behind Caching はキャッシュのみを更新し、DB を直接更新せず、代わりに DB を更新します。非同期バッチモードで。
明らかに、この方法はデータの一貫性に対して大きな課題をもたらします。たとえば、キャッシュ データが DB に対して非同期に更新されない場合、キャッシュ サービスがハングする可能性があり、より大きな惨事を引き起こす可能性があります。 この戦略も、日常の開発プロセスでは非常にまれですが、アプリケーション シナリオがほとんどないというわけではありません。たとえば、メッセージ キュー内のメッセージのディスクへの非同期書き込みや、MySQL の InnoDB バッファ プール メカニズムなどです。誰もがこの種の戦略を使用します。 Write Behind パターン DB の書き込みパフォーマンスは非常に高く、データが頻繁に変更され、ビューやいいねの数など、データの一貫性要件がそれほど高くない一部のシナリオに非常に適しています。 バイパス キャッシュ モードの分析キャッシュ アサイド パターンに関するいくつかの質問バイパス キャッシュ モードは、私たちが日常生活で最もよく使用するモードです。上記で紹介したバイパス キャッシュ モードに基づいて、次のような疑問が生じるかもしれません。書き込み操作でキャッシュが更新されずにキャッシュが削除される理由
回答: スレッド A が最初に書き込み操作を開始し、最初にそれを更新します。データベース。スレッド B が別の書き込み操作を開始し、2 番目のステップでデータベースを更新します。ネットワークなどの理由により、スレッド B が最初にキャッシュを更新し、スレッド A がキャッシュを更新します。
このとき、キャッシュにはAさんのデータ(古いデータ)が保存され、データベースにはBさんのデータ(新しいデータ)が保存されており、不整合となりダーティなデータが現れます。 がキャッシュ を更新する代わりにキャッシュを削除すると、このダーティ データの問題は発生しません。
実際、書き込み操作が必要な場合にキャッシュを更新することは可能ですが、キャッシュを更新するときにスレッドの安全性の問題が発生しないようにするために、ロック/分散ロックを追加する必要があります。データの書き込みプロセスで、最初に DB を更新してからキャッシュを削除する必要があるのはなぜですか?
回答: たとえば、 、リクエスト 1 は書き込み操作です。最初にキャッシュ A を削除すると、リクエスト 2 は読み取り操作です。まずキャッシュ A を読み取り、キャッシュが削除されたことを確認して (リクエスト 1 で削除された)、その後データベースを読み取りますが、この時点ではリクエスト 1 にはデータを更新する時間がなかったので、リクエスト 2 で読み取られたのは古いデータであり、リクエスト 2 では読み取られた古いデータもキャッシュに入れられるため、データの不整合が発生します。
実際には、最初にキャッシュを削除してからデータベースを更新することも可能です。たとえば、遅延二重削除戦略を採用する場合は、1 秒間スリープしてからデータベースを更新します。再度キャッシュを削除すると、1秒以内にすべてのデータを削除できます。これにより発生したキャッシュされたダーティデータは再度削除されます。 1 秒である必要はありません。ビジネスによって異なります。
ただし、この 1 秒間には多くの要因が発生する可能性があり、不確実性が大きすぎるため、このアプローチはお勧めできません。
回答:
理論上は、データの不整合が依然として発生する可能性がありますが、その可能性は非常に小さいです。 2 つのリクエストがあり、1 つは A にクエリ操作の実行を要求し、もう 1 つは B に更新操作の実行を要求すると仮定すると、次の状況が発生します。
(1) キャッシュの有効期限が切れたばかりです
(2) A にデータベースにクエリを実行して古い値を取得するようリクエストします
(3) B に新しい値をデータベースに書き込むようリクエストします
(4) リクエストB がキャッシュを削除する
(5) A が見つかった古い値をキャッシュに書き込むよう要求します。上記の状況が発生すると、確かにダーティ データが発生します。
ただし、これが発生する確率は高くありません。
上記の状況が発生する先天的な条件があります。つまり、ステップ ( 3) は手順 (2) のデータベース読み取り操作よりも時間が短いため、手順 (4) を手順 (5) よりも先に実行することができます。
しかし、よく考えてみると、データベースの読み取り操作は書き込み操作よりもはるかに高速です (そうでない場合、なぜ読み取りと書き込みを分離するのでしょうか? 読み取りを分離する意味書き込みは、読み取り操作の方が高速で、消費するリソースが少ないためです)。そのため、ステップ (3) はステップ (2) よりも時間がかからず、この状況は発生しにくくなります。
不一致の理由は他にありますか?
回答: キャッシュの削除に失敗すると、不整合が発生します。
これを解決するにはどうすればよいですか?
Canal を使用してデータベースの binlog をサブスクライブし、操作する必要があるデータを取得します。別のプログラムを起動して、このサブスクリプション プログラムから情報を取得し、キャッシュを削除してください。
欠陥 1: 最初に要求されたデータはキャッシュ内にあってはなりません
解決策: ホット データをキャッシュに入れることができますキャッシュ内で前進します。
欠点 2: 書き込み操作が頻繁に行われると、キャッシュ内のデータが頻繁に削除され、キャッシュ ヒット率に影響します。
データベースとキャッシュ データ間の強力な整合性シナリオ: DB を更新すると、キャッシュも更新されますが、更新時にスレッドの安全性の問題が発生しないようにロック/分散ロックを追加する必要があります。キャッシュ。データベースとキャッシュ データが不整合であるシナリオを一時的に許可できます: DB を更新すると、キャッシュも更新されますが、比較的短い有効期限がキャッシュに追加されます。これにより、データが不整合であっても、影響が確実に発生します。比較的小さいものになります。
以上がRedis と MySQL 間の二重書き込み問題を解決する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。