この記事では、Redis がキャッシュの不整合の問題をどのように解決するか、およびキャッシュとデータベースの間でデータの不整合がどのように発生するかを説明します。一緒に見てみましょう。皆さんのお役に立てれば幸いです。 . .
推奨学習: Redis 学習チュートリアル
まず、「データの整合性」とは具体的に何を意味するのかを理解する必要があります。実際、ここでの「一貫性」には 2 つの状況が含まれます:
読み取り/書き込みキャッシュの場合、データを追加、削除、または変更する場合は、キャッシュ内で行う必要があります。同時に、同期ベースでデータをデータベースに書き戻すかどうかを決定する必要があります。採用されたライトバック戦略について。
同期直接書き込み戦略: キャッシュを書き込むとき、データベースも同期的に書き込まれ、キャッシュ内のデータとデータベースの一貫性が保たれます。
非同期ライトバック戦略:キャッシュにデータが書き込まれるまで、データベースは同期的に書き込まれません。キャッシュからデータが削除されると、データはデータベースに書き戻されます。この戦略を使用する場合、データがデータベースに書き戻されていない場合、キャッシュは失敗し、この時点ではデータベースには最新のデータがありません。
したがって、読み取り/書き込みキャッシュの場合、キャッシュ内のデータとデータベースの一貫性を確保したい場合は、同期直接書き込み戦略を採用する必要があります。ただし、この戦略を採用する場合は、キャッシュとデータベースを同時に更新する必要があることに注意してください。したがって、ビジネス アプリケーションでトランザクション メカニズムを使用して、キャッシュとデータベースの更新がアトミックであることを保証する必要があります。つまり、両方が同時に更新されるか、どちらも更新されず、エラー メッセージが返され、再試行が実行されます。そうしないと、同期直接書き込みを実現できません。
もちろん、シナリオによっては、データの一貫性に対する要件がそれほど高くない場合もあります。たとえば、電子商取引商品の重要ではない属性や、短いビデオの作成時間や変更時間をキャッシュする場合、非同期ライトバック戦略を使用できます。
読み取り専用キャッシュについて話しましょう。読み取り専用キャッシュの場合、新しいデータがある場合はデータベースに直接書き込まれます。データが削除された場合は、読み取り専用キャッシュ内のデータを無効としてマークする必要があります。このように、アプリケーションがその後、これらの追加、削除、または変更されたデータにアクセスすると、キャッシュ内に対応するデータがないため、キャッシュ ミスが発生します。このとき、アプリケーションはデータベースからデータをキャッシュに読み取ります。これにより、後でデータにアクセスするときに、キャッシュから直接データを読み取ることができます。
次に、次の図に示すように、Tomcat による MySQL へのデータの書き込みと削除を例として、データの追加、削除、および変更操作がどのように実行されるかを説明します。図からわかるように、Tomcat 上で実行されているアプリケーションは、データ X の追加 (Insert 操作)、変更 (Update 操作)、または削除 (Delete 操作) のいずれであっても、データベース内のデータを直接追加、変更、および削除します。もちろん、アプリケーションが変更または削除操作を実行すると、キャッシュされたデータ X も削除されます。
では、このプロセスでデータの不整合は発生するのでしょうか?データの追加と削除では状況が異なるため、分けて見ていきます。
新しいデータ新しいデータの場合、データはキャッシュに対する操作を行わずにデータベースに直接書き込まれます。この時点では、キャッシュ自体には新しいデータはありません。この状況は、先ほど述べた整合性の 2 番目の状況と一致するため、この時点ではキャッシュとデータベースのデータは整合しています。
アプリケーションは、データ X の値を 10 から 3 に更新したいと考えています。まず、Redis キャッシュ内の X のキャッシュ値を削除しますが、データベースの更新は失敗します。他に同時アクセス要求がある場合
最初にデータベースを更新してからキャッシュ内の値を削除すれば、この問題は解決できるのかと疑問に思われるかもしれません。もう一度分析してみましょう。
アプリケーションが最初にデータベースの更新を完了したが、キャッシュの削除に失敗した場合、データベース内の値は新しい値であり、キャッシュ内の値は古い値であり、これは明らかに矛盾しています。このとき、データにアクセスする他の同時リクエストがある場合、通常のキャッシュ アクセス プロセスに従って、最初にキャッシュがクエリされますが、この時点では古い値が読み取られます。
例を使って説明しましょう。
アプリケーションはデータ X の値を 10 から 3 に更新したいと考えています。最初にデータベースが正常に更新され、次に Redis キャッシュ内の X のキャッシュが削除されますが、この操作は失敗しました。この時点で、データベース内の X の新しい値は 3 ですが、Redis にキャッシュされた X の値は 10 であり、これは明らかに矛盾しています。この時点で別のクライアントがたまたま X にアクセスするリクエストを送信した場合、最初に Redis でクエリを実行し、クライアントはキャッシュ ヒットを見つけますが、古い値 10 が読み取られます。
わかりました。ここでは、データベースの更新とキャッシュされた値の削除のプロセスで、2 つの操作のどちらが先に実行されるか後で実行されるかに関係なく、一方の操作が失敗する限り、クライアントに問題が発生することがわかります。古い価値観を読むためです。先ほど述べた 2 つの状況を要約して、以下の表を作成しました。
問題の原因はわかっていますが、どうすれば解決できますか?
まず最初に、再試行メカニズムというメソッドを紹介します。
具体的には、削除するキャッシュ値または更新するデータベース値をメッセージ キューに一時的に保存できます (たとえば、Kafka メッセージ キューを使用)。アプリケーションがキャッシュされた値の削除またはデータベース値の更新に失敗した場合、メッセージ キューから値を再度読み取り、再度削除または更新することができます。
削除または更新が成功した場合は、操作の繰り返しを避けるために、これらの値をメッセージ キューから削除します。このとき、データベースとキャッシュされたデータの一貫性も確保できます。それ以外の場合は、もう一度試す必要があります。一定の回数を超えても再試行が失敗する場合は、ビジネス層にエラー メッセージを送信する必要があります。
次の図は、データベースを更新してからキャッシュ値を削除する場合で、キャッシュの削除に失敗した場合は、リトライして削除が成功する様子を確認できます。
私が今話したのは、データベースの更新とキャッシュされた値の削除のプロセス中に操作の 1 つが失敗する状況です。実際、これら 2 つの操作が失敗したとしても、初めて実行されました どれも失敗しませんでした 多数の同時リクエストがある場合、アプリケーションは一貫性のないデータを読み取る可能性があります。
同様に、削除順序と更新順序の違いに応じて 2 つの状況に分けます。どちらの場合でも、ソリューションも異なります。
スレッド A がキャッシュ値を削除した後、データベースを更新する前にスレッド B がデータの読み取りを開始するとします (たとえば、ネットワークの遅延がある)。スレッド B はキャッシュが見つからないことがわかり、データベースからの読み取りのみが可能になります。これにより 2 つの問題が発生します:
スレッド B がデータベースからデータを読み取ってキャッシュを更新するまで待ち、その後スレッド A がデータベースの更新を開始します。この時点で、キャッシュ内のデータは古い値ですが、データはデータベース内の は最新の値です。この 2 つは矛盾しています。
この状況を表にまとめてみました。 ############私たちは何をすべきか?解決策をご紹介します。
スリープ期間を追加する理由は、スレッド B が最初にデータベースからデータを読み取り、次に欠落したデータをキャッシュに書き込んでから、スレッド A がそのデータを削除できるようにするためです。したがって、スレッド A がスリープする時間は、スレッド B がデータを読み取ってキャッシュに書き込む時間よりも長くする必要があります。今回はどうやって決めるのでしょうか?業務プログラム実行時にデータの読み込みやキャッシュの書き込みを行うスレッドの動作時間を計測し、見積もることをお勧めします。 このようにして、他のスレッドがデータを読み取るときに、キャッシュが欠落していることがわかり、データベースから最新の値を読み取ります。このソリューションでは、キャッシュされた値を初めて削除した後、一定期間削除が遅れるため、「遅延二重削除」とも呼ばれます。 次の疑似コードは、「遅延二重削除」スキームの例です。ご覧ください。 シナリオ 2: 最初にデータベース値を更新し、次にキャッシュ値を削除します。 スレッド A がデータベース内の値を削除したが、キャッシュ値を削除する前に、スレッド B がデータの読み取りを開始した場合、スレッド B がキャッシュにクエリを実行すると、キャッシュ ヒットが見つかりました。古い値がキャッシュから直接読み取られます。ただし、この場合、他のスレッドからキャッシュを読み取る同時リクエストがそれほど多くなければ、古い値を読み取るリクエストも多くありません。また、スレッド A は通常、キャッシュされた値をすぐに削除するため、別のスレッドが再度読み取るとキャッシュ ミスが発生し、最新の値がデータベースから読み取られます。したがって、この状況がビジネスに与える影響はほとんどありません。 最初にデータベースを更新してからキャッシュされた値を削除する状況を示すために、別の表を描画します。 さて、キャッシュとデータベース間のデータの不整合は、一般に 2 つの理由によって引き起こされることがわかりました。対応する解決策を提供しました。 推奨される学習: Redis ビデオ チュートリアルredis.delKey(X)
db.update(X)
Thread.sleep(N)
redis.delKey(X)
以上がRedis はキャッシュの不整合の問題をどのように解決しますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。