1. ホーム
  2. データベース
  3. その他のデータベース

MySQLとRedisがデータの一貫性を確保する方法について説明します。

2022-01-17 20:04:59

前置き

キャッシュの高い並行性と性能は様々なプロジェクトで広く利用されているので、このキャッシュの読み込みという側面は基本的に同じで、おそらく以下の図のような流れになると思われます。

しかし、キャッシュの更新という点では、データベースを更新した後にキャッシュを更新した方がいいのか、それともキャッシュを削除すればいいのでしょうか?それとも、キャッシュを削除してからデータベースを更新したほうがいいのでしょうか?現時点では模索する価値がありそうです。

一貫したソリューション

実際のプロジェクト開発では、データベースとキャッシュのデータが一貫していることを確認する必要があります。そうでなければ、人々が100ドルをトップアップして更新し続けても、0.01ドルを表示するのは厄介ではないでしょうか?理論的には、キャッシュに有効期限を設定することは、データの一貫性を確保するための究極の解決策です。このソリューションでは、すべての書き込み操作はデータベースに基づいて行われ、データベースの書き込みが成功してもキャッシュの更新が失敗した場合、キャッシュは自然にデータベースの新しい値を読み、有効期限後に読み込まれたときにキャッシュを更新します。次に、キャッシュの有効期限設定に頼らずに、いかにしてデータの一貫性を確保するかというアイデアがある。ここでは、主に3つの選択肢を検討する。

<ブロッククオート

1) まずデータベースを更新し、次にキャッシュを更新する
キャッシュを削除してから、データベースを更新する。
先にデータベースを更新し、その後キャッシュを削除する。

最初にデータベースを更新し、次にキャッシュを更新します。

この解決策は(私の知る限り~)一般的に反対されていますが、なぜですか?なぜこの選択肢は反対されるのでしょうか?主な理由は2つあり、それについて詳しく説明します。

まず、データセキュリティの観点から、Aの要求とBの要求が同時にあり、Aが先にデータベースのデータを更新し、すぐにBがそのデータを更新した場合、おそらくネットワークの遅延などの理由で、AよりBが先にキャッシュを更新した場合、その後どうなるでしょうか。キャッシュのデータはBが更新した最新のデータではないので、データの不整合が発生します。

次に、ビジネスシナリオの面では、データベースへの書き込みが多く、読み込みが少ないビジネスの場合、このソリューションを使うと、キャッシュを読み込む前にデータが頻繁に更新されることになり、パフォーマンスを無駄にすることになります。

キャッシュを削除してからデータベースを更新するのか、データベースを更新してからキャッシュを削除するのか、次の2つのシナリオがより議論を呼ぶでしょう。

データベースを更新する前にキャッシュを削除する場合

Aの更新要求とBの問い合わせ要求が同時にあった場合、Aが書き込み操作を要求する前にキャッシュを削除し、ちょうどその時にBが入ってきてキャッシュが空であることに気づき、Bがデータベースに問い合わせ、Aの書き込み操作がまだ完了していなければBは古い値をまだ問い合わせるか、古い値をキャッシュに書き込み、Aが新しい値をデータベースに書き込むということが起こる可能性があるのだそうです。キャッシュに有効期限を設けない場合、データは常にダーティになります。

この状況を解決するには、遅延二重削除戦略を使用します。これは、データベースを更新する前にキャッシュを削除し、その後データベースに書き込み、データベースの更新後に再びキャッシュを削除することで、読み取り要求によって引き起こされる可能性のあるキャッシュされたダーティデータを削除する方法です。このようにする目的は、読み取り要求が終了し、書き込み要求が読み取り要求によって引き起こされたダーティデータを削除できるようにするためである。MySQLは、読み取り/書き込みアーキテクチャを使用している場合、データの不整合は、マスタースレーブの遅延によって引き起こされる可能性があり、これはマスタースレーブ遅延時間ハイバネーションによると書き込み操作の完了後に行うことができますし、キャッシュ操作を削除することができます。遅延二重削除の疑似コードは、次のとおりです。

# Pseudocode
def delay_delete():
    redis.delete('name') # delete the cache before updating the database
    sql = 'update info set name='lili' where id=1;' # update database
    cursor.execute(sql)  
    time.sleep(1) # If mysql is a master-slave architecture then sleep the master-slave delay time for a few hundred ms more
    redis.delete('name') # Delete the cache again


キャッシュ削除の2回目の失敗はあるのでしょうか?もし2回目の削除に失敗したら、やはりキャッシュとデータベースの間に矛盾が生じることになります。では、どのように修正すればいいのでしょうか?次のオプションを見てみましょう。

先にデータベースを更新し、その後キャッシュを削除します。

外国人が思いついたのは キャッシュ更新方式 キャッシュAsidepatternCache - SidepatternCache - Sidepatternは、記事の言及**アプリケーションは、フェッチが成功した場合、キャッシュからデータを取得する必要がありますし、データベースから直接返す、ない場合は、取得、キャッシュへの成功後、データを更新するには、最初の成功後にデータベースにデータを保存し、キャッシュの有効期限が切れるようにする必要があります。**元のテキストは次のとおりです。

アプリケーションが情報を更新する場合、データストアに変更を加え、キャッシュの対応する項目を無効にすることで、ライトスルー戦略に従うことができます。
次にその項目が必要になったとき、キャッシュ・アサイド戦略を使用すると、更新されたデータがデータストアから取得され、キャッシュに再び追加されることになる。

このソリューションでは、データの不整合は発生しないのでしょうか?例えば、以下のような場合です。

AとBの2つのリクエストがあり、Aはクエリを行い、Bは更新を行うので、次のようなことが起こったとします。

1) キャッシュがちょうど期限切れになった

リクエストAは、データベースにアクセスし、古い値を取得します。

(iii) リクエストBは、新しい値をデータベースに書き込む。

4.書き込み成功後、Bにキャッシュの削除を依頼する

リクエストAはチェックしたメカニズムをキャッシュに書き込み、ダーティデータを発生させる...。

上記を発声すると、確かに不整合なデータが出ますが、XDMはこのようなことが起こる確率を考えてください。仮にこの結果を先に出すとしたら、リクエストBの操作時間が非常に短い、どこまで短いか、つまりリクエストAのデータベースからデータを読み込む操作より、リクエストBのデータベースに書き込む操作の方が速い(redisは非常に速いので、redisの操作時間はとりあえず無視できる)、この場合のみ⑤より先に④が発声できる、しかしデータベースの読み込み操作が書き込み操作よりはるかに速い、そうしないとなぜ読み書きを分離するのか、という条件も必要でしょう。というわけで、このようなことが起こる確率はとてもとても低いのですが、もし強迫性障害の患者さんが登場したら、それを解決しなければならないのでしょうか?あなたは、有効期限を設定するためにキャッシュを使用するか、削除操作の後に読み取り要求が完了することを保証するために遅延二重削除戦略の第二のオプションを使用することができます。

最後の質問

まだ問題があります。つまり、解決策3で発生しうる非常に低い確率のデータ不整合に対する最終的な解決策は、解決策2の遅延ダブル削除戦略ですが、解決策2にも書かれているように、キャッシュ削除に失敗した場合はどうするのでしょうか?やはりデータ不整合の問題になるのでは?最終的にこの問題は、どのようにそれを解決するには?ここでは、再試行のメカニズムを提供するために、再試行の失敗を削除するには、ここで再試行の解決策を提供する。

<ブロッククオート

データベースを更新する
キャッシュの削除に失敗しました。
失敗したキャッシュ削除をメッセージキューに入れる。
ビジネスコードはメッセージキューから削除するキーを取得します。
削除操作が成功するまで試行錯誤を続ける。

概要

今回の記事は、MySQLとRedis間のデータの整合性を確保する方法についてです。MySQLとRedisのデータ整合性については、スクリプトハウスの過去記事を検索していただくか、引き続き以下の関連記事をご覧ください。