ホームページ  >  記事  >  データベース  >  Redis と MySQL の二重書き込みキャッシュが矛盾している場合はどうすればよいですか?ソリューションの共有

Redis と MySQL の二重書き込みキャッシュが矛盾している場合はどうすればよいですか?ソリューションの共有

青灯夜游
青灯夜游転載
2022-03-15 11:18:292387ブラウズ

Redis と MySQL の二重書き込みキャッシュに矛盾がある場合はどうすればよいですか?この記事では、キャッシュの二重書き込みの不一致の問題を解決する方法を紹介します。

Redis と MySQL の二重書き込みキャッシュが矛盾している場合はどうすればよいですか?ソリューションの共有

redis と mysql の二重書き込みキャッシュには一貫性がありません:

Redis と MySQL の二重書き込みキャッシュが矛盾している場合はどうすればよいですか?ソリューションの共有

ただし、キャッシュの更新に関しては、 データベースの更新が完了しました。キャッシュを更新するべきですか、それともキャッシュを削除すべきですか? または まずキャッシュを削除してからデータベースを更新します。実際には、多くの議論があります。 現時点では、これらのソリューションを分析する包括的なブログはありません。そこでブロガーは、みんなから批判される危険を冒して震えながらこの記事を書きました。

#テキスト

キャッシュされたデータの有効期限を設定する

最初に説明します。理論的には、キャッシュの有効期限は、最終的な整合性を確保するためのソリューションです。このソリューションでは、キャッシュに保存されたデータの有効期限を設定でき、すべての書き込み操作はデータベースの影響を受けるため、キャッシュ操作にのみ最善を尽くす必要があります。つまり、データベースの書き込みが成功し、キャッシュの更新が失敗した場合、有効期限に達している限り、後続の読み取りリクエストは自然にデータベースから新しい値を読み取り、キャッシュをバックフィルします。したがって、次に説明するアイデアは、キャッシュの有効期限を設定するという解決策に依存しません。

ここでは、最初に 3 つの

更新戦略について説明します:

    最初にデータベースを更新し、次にキャッシュを更新します
  1. 最初にキャッシュを削除します。次にデータベースを更新します。
  2. 最初にデータベースを更新してからキャッシュを削除します
最初にデータベースを更新してからキャッシュを更新します

この計画は一般に誰もが反対します。なぜ?ポイントは以下の 2 点です。

    理由 1 (スレッド安全性の観点)
(1) スレッド A がデータベースを更新した

(2) スレッド B がデータベースを更新したデータベース
(3) スレッド B がキャッシュを更新しました
(4) スレッド A がキャッシュを更新しました

つまり、A にキャッシュの更新を要求することは、B にキャッシュの更新を要求するよりも先に行う必要がありますが、ただし、ネットワークなどの理由により、B は A よりも早くキャッ​​シュを更新しました。これはデータがダーティになるため、考慮されません。

    理由 2 (ビジネス シナリオの観点)
(1) データベース書き込みシナリオが多く、データ読み取りシナリオが少ないビジネス ニーズがある場合は、これを使用してください。このソリューションでは、次のような問題が発生します。データが読み取られる前にキャッシュが頻繁に更新されるため、パフォーマンスが無駄になります。

(2) データベースに書き込む値がキャッシュに直接書き込まれない場合は、一連の複雑な計算を経てからキャッシュに書き込まれる必要があります。次に、データベースに書き込まれるたびにキャッシュに書き込まれる値を再計算することは、間違いなくパフォーマンスの無駄です。明らかに、キャッシュを削除する方が適切です。

最初にキャッシュを削除してからデータベースを更新してください

この解決策が不整合を引き起こす理由は次のとおりです。同時に、1 つは A に更新操作の実行を要求し、もう 1 つは B にクエリ操作の実行を要求します。次に、次の状況が発生します。

(1) A に書き込み操作の実行とキャッシュの削除を要求します

(2) B にクエリを要求し、キャッシュが存在しないことを確認します
(3 ) B にデータベースにクエリを実行して古い値を取得するように要求します
(4) B に古い値をキャッシュに書き込むように要求します
(5) A に新しい値をデータベースに書き込むように要求します

上記の状況では不整合が発生します。さらに、キャッシュの有効期限戦略を設定しない場合、データは常にダーティ データになります。

それでは、どうすれば解決できるでしょうか?遅延二重削除戦略を採用する

キャッシュ遅延二重削除
public class CacheServiceImpl implements ICacheService {

    @Resource
    private RedisOperator redisOperator;
    
    @Autowired
    private IShopService shopService;

    //1. 采用延时双删,解决数据库和缓存的一致性
    @Override
    public void updateHotCount(String id) {
        try {
            //删除缓存
            redisOperator.del("redis_key_" + id);
            //更新数据库
            shopService.updataHotShop();
            Thread.sleep(1000);//休眠1秒
            //延时删除
            redisOperator.del("redis_key_" + id);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }

    @Override
    public Integer getHotCount(String id) {
        return null;
    }}

説明:

    最初にキャッシュを削除します
  1. データベースを再度書き込みます
  2. 1秒スリープしてからキャッシュを削除します (
  3. これにより、キャッシュされたダーティデータを再度1秒以内に削除できます。)

上記の状況を考慮して、読者は自分のプロジェクトの時間のかかるデータ読み取りビジネス ロジックを評価する必要があります。データ書き込みのスリープ時間は、データ ビジネス ロジックの読み取りにかかる時間に数百ミリ秒を加えたものに基づいています。この目的は、読み取りリクエストが確実に終了し、書き込みリクエストによって読み取りリクエストによって発生したキャッシュされたダーティ データを削除できるようにすることです。

データベースが読み取りと書き込みの分離アーキテクチャを採用している場合はどうなるでしょうか? (メイン ライブラリは書き込み操作を担当し、スレーブ ライブラリは読み取り操作を担当します)

わかりました。この場合、データの不一致の理由は次のとおりです。まだ 2 つのリクエストがあり、1 つのリクエスト A は更新操作を実行し、もう 1 つのリクエストは B にクエリ操作を実行します。

(1) A に書き込み操作の実行を要求し、キャッシュを削除し、A にメイン ライブラリへのデータの書き込みを要求します。スレーブ ライブラリからの同期はまだ開始されていません

(2) (1秒以内) B にキャッシュのクエリを要求、キャッシュが見つからず、B にスレーブ データベースのクエリを要求 この時点ではマスタとスレーブの同期は完了しておらず、古い値が見つかり、古い値がキャッシュに書き込まれます。

(3) マスター データベースはマスターとスレーブの同期を完了し、スレーブ データベースは新しい値に変更されます。

上記のプロセスでは、データの不整合と二重削除の遅延が問題になります。という戦略も使われます。ただし、スリープ時間は、マスターとスレーブの同期遅延時間に数百ミリ秒を加えたものに基づいて変更されます。

この同期排除戦略を採用した場合、スループットが低下した場合はどうすればよいですか?

わかりました。2 番目の削除を非同期として扱います。自分でスレッドを開始し、非同期的に削除します。このようにして、書き込まれたリクエストは返される前に一定期間スリープする必要がありません。そうすることでスループットが向上します。

削除が 2 回目に失敗した場合はどうすればよいですか?

これは非常に良い質問です。削除が 2 回目に失敗すると、次のような状況が発生します。まだ 2 つのリクエストがあり、1 つは更新操作の実行を A にリクエストし、もう 1 つは B にクエリ操作の実行をリクエストしています。便宜上、単一のデータベースであると仮定します。

(1) Request A to書き込み操作を実行してキャッシュを削除します
( 2) B にクエリを要求し、キャッシュが存在しないことがわかります
(3) B にデータベースにクエリを実行して古い値を取得するように要求します
(4) 要求B が古い値をキャッシュに書き込むように要求します
(5) A が新しい値をデータベースに書き込むように要求します
(6) 要求 A は、要求 B によって書き込まれたキャッシュ値を削除しようとしましたが、失敗しました。

わかりました、つまり。 2 回目にキャッシュの削除に失敗すると、再びキャッシュとデータベースの不整合が発生します。

どうすれば解決できますか?

具体的な解決策については、最初にデータベースを更新してからキャッシュを削除するという更新戦略に関するブロガーの分析を見てみましょう。

キャッシュの再試行メカニズムの削除

遅延二重削除キャッシュアサイドは、最初にデータベースを操作してからキャッシュを削除します。 、2 番目の手順でキャッシュを削除できなかったことにより、データの不整合の問題が発生する可能性があります。このソリューションを使用して最適化することができます。削除に失敗した場合は、さらに数回削除して、キャッシュの削除が確実に成功するようにします。したがって、削除キャッシュ再試行メカニズム

を導入できます。 Redis と MySQL の二重書き込みキャッシュが矛盾している場合はどうすればよいですか?ソリューションの共有

    ##(1) データベースのデータを更新します;
  1. (2) さまざまな問題によりキャッシュの削除に失敗しました
    (3) 削除する必要のあるキーをに送信しますメッセージキュー
    (4) メッセージを自分で消費し、削除する必要があるキーを取得します
    (5) 成功するまで削除操作を再試行し続けます
ただし、この解決策はには欠点があり、ビジネスラインコードへの多くの侵入を引き起こします。 2 番目のオプションでは、サブスクリプション プログラムを開始してデータベースのバイナリログをサブスクライブし、操作する必要のあるデータを取得します。アプリケーションで、新しいプログラムを起動して、このサブスクリプション プログラムから情報を取得し、キャッシュを削除します。

biglog の読み取りとキャッシュの非同期削除

Redis と MySQL の二重書き込みキャッシュが矛盾している場合はどうすればよいですか?ソリューションの共有

プロセスは次の図に示すとおりです。 1) データベース データを更新します

(2) データベースは操作情報を binlog ログに書き込みます

(3) サブスクリプション プログラムは必要なデータとキーを抽出します

(4) 非データベースの新しいセクションを開始します情報を取得するためのビジネス コード
(5) キャッシュ操作の削除を試みますが、削除に失敗したことがわかります。
(6) 情報をメッセージ キューに送信します。
(7) データをメッセージ キューから再取得します。メッセージキューを削除して、操作を再試行してください。

備考: 上記の binlog サブスクリプション プログラムには、mysql に canal と呼ばれる既製のミドルウェアがあり、binlog ログをサブスクライブする機能を完了できます。 Oracle については、ブロガーは現在、使用できる既製のミドルウェアがあるかどうかを知りません。また、リトライ機構として、ブロガーはメッセージキューを使用します。一貫性の要件がそれほど高くない場合は、プログラム内で新しいスレッドを開始し、時々再試行してください。これは柔軟に使用できますが、ここではアイデアを提供します。

この記事は、実際には、インターネット上の既存の整合性ソリューションの概要です。最初にキャッシュを削除してからデータベースを更新する更新戦略については、メモリキューを維持する計画もありますが、ブロガーはそれを見て、実装が非常に複雑で不必要であると感じたので、与える必要はありません記事の中で。最後に、皆さんが何かを得られたことを願っています。

[関連する推奨事項:

mysql ビデオ チュートリアル

]

以上がRedis と MySQL の二重書き込みキャッシュが矛盾している場合はどうすればよいですか?ソリューションの共有の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjianshu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。