[redis] redisキャッシュとデータベース間の整合性

[1] 4 つの同期戦略

キャッシュとデータベースの二重書き込みを一貫して行うには、4 つの方法、つまり 4 つの同期戦略があります:
(1) 最初にキャッシュを更新し、次にデータベースを更新します。
(2) 最初にデータベースを更新し、次にデータベースを更新します。キャッシュ;
(3) 最初のキャッシュを削除してからデータベースを更新;
(4) 最初にデータベースを更新してからキャッシュを削除します。

キャッシュを更新するのとキャッシュを削除するのはどちらがより適切ですか? データベースを先に操作するべきですか、それともキャッシュを先に操作するべきですか?

【2】キャッシュの更新またはキャッシュの削除

(1) キャッシュの更新

(1) メリット
データの変更に合わせてキャッシュが更新されるため、クエリの実行を見逃しにくくなります。

(2) デメリット
キャッシュ更新の消費量が比較的多くなります。複雑な計算の後にデータをキャッシュに書き込む必要がある場合、キャッシュを頻繁に更新するとサーバーのパフォーマンスに影響します。データが頻繁に書き込まれるビジネス シナリオの場合、キャッシュが頻繁に更新されるとデータを読み取るビジネスがなくなる可能性があります。

(2) キャッシュを削除する

(1) メリット
複雑な更新操作であっても、キャッシュ上のデータを直接削除するため、操作が簡単です。

(2) デメリット
キャッシュを削除すると、次のクエリキャッシュが失われ、データベースを再度読み込む必要があります。上記の比較から、一般に、キャッシュを削除する方が良い解決策となります。

[3] 最初にデータベースを更新するか、最初にキャッシュを削除します

(1) 障害が発生した場合

まず、最初にキャッシュを削除し、最初にデータベースを更新し、障害が発生した場合に比較を行います。

1- 最初にキャッシュを削除してからデータベースを更新します (データベースの更新に失敗しました)

最初にキャッシュを削除してからデータベースを更新します。失敗した場合に考えられる問題:
(1) スレッド A はキャッシュを正常に削除しますが、スレッド A はデータベースの更新に失敗します。
(2) スレッド B はキャッシュからデータを読み取ります。が削除され、プロセス B キャッシュからデータを取得できず、データベースから読み取ることができません。このとき、データベース内のデータの更新は失敗し、スレッド B はデータベースから古いデータを正常に取得し、データを更新します。キャッシュに。
(3) 最終的に、キャッシュとデータベースのデータは一貫していますが、古いデータのままです。

ここに画像の説明を挿入

2-最初にデータベースを更新してからキャッシュを削除します(キャッシュの削除に失敗しました)

最初にデータベースを更新してからキャッシュを削除します。失敗した場合に考えられる問題:
(1) スレッド A はデータベースを正常に更新しましたが、スレッド A はキャッシュの削除に失敗しました。
(2) スレッド B はキャッシュを正常に読み取りましたが、キャッシュが削除されたため、削除は失敗し、スレッド B が読み取ります。 フェッチされるのはキャッシュ内の古いデータです。
(3) 最後に、スレッド A はキャッシュを正常に削除し、他のスレッドはキャッシュ内の同じデータ (データベース内のデータと同じ) にアクセスします。
(4) 最終的に、キャッシュとデータベースのデータは一貫していますが、一部のスレッドは古いデータを読み取ります。

ここに画像の説明を挿入

3- まとめ

上記の比較の結果、障害が発生した場合、キャッシュを削除してからデータベースを削除するのが先か、データベースを更新してから実行するのがどちらが良いのか明確に区別することはできず、どちらにも問題があると考えられます。上記のシナリオの問題を解決するにはどうすればよいでしょうか? これを解決するには、再試行メカニズムを使用することをお勧めします。

(2) 故障がない場合

1- まずキャッシュを削除してからデータベースを更新します

(1) スレッド A はキャッシュの削除に成功しました。
(2) スレッド B はキャッシュの読み取りに失敗しました。
(3) スレッド B はデータベースの読み取りに成功し、古いデータを取得しました。
(4) スレッド B は古いデータをキャッシュに正常に更新しました
。 5) スレッド A は、新しいデータをデータベースに正常に更新します。
ここに画像の説明を挿入プロセス A の 2 ステップの操作は成功していますが、同時実行性の存在により、プロセス B がこれら 2 つのステップの間にキャッシュにアクセスしていることがわかります。最終的に、古いデータはキャッシュに保存され、新しいデータはデータベースに保存され、この 2 つのデータ間のデータは不整合になります。

遅延二重削除はこの問題を解決します

キャッシュを削除してからデータベースを更新すると、障害がない場合でもデータの不整合が発生する可能性があります。実際のアプリケーションで、いくつかの考慮事項によりこの方法を選択する必要がある場合は、遅延二重削除戦略を採用できます。遅延二重削除の基本的な考え方は次のとおりです: (1) キャッシュを削除する; (2)データベース
を更新します; ( 3) N ミリ秒間スリープします; (4) キャッシュを再度削除します。


public void write(String key, Object data) {
    
    
    Redis.delKey(key);
    db.updateData(data);
    Thread.sleep(1000);
    Redis.delKey(key);
}

一定期間ブロックした後、再度キャッシュを削除して、このプロセス中にキャッシュ内の矛盾したデータを削除します。具体的な時間については、業務のおおよその時間を評価し、それに合わせて設定するだけです。最後に、データベースとキャッシュ間のデータの一貫性が保証されます。

読み取りと書き込みが分離されているアーキテクチャ (メイン ライブラリの読み取りが必須) の場合はどうなるでしょうか。

データベースが読み書き分離アーキテクチャを採用すると、下図のように新たな問題が発生します。
このとき、リクエストA(更新操作)とリクエストB(クエリ操作)の2つが存在します。
(1)リクエストA更新操作、削除された Redis;
(2) マスター データベースに更新操作の実行を要求すると、マスター データベースとスレーブ データベースはデータを同期します。( 3)
B にクエリを依頼し、Redis にデータがないことを確認します。
(4) ) スレーブデータベースから取得 (5) この時点では
主従同期データが完成しておらず、取得したデータは古いデータです。

ここに画像の説明を挿入
現時点での解決策は、データを埋めるためにデータベースにクエリを実行する場合、Redis がクエリ用のメイン データベースを指すように強制することです。

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

それでも削除に失敗する場合は、リトライ回数を増やすこともできますが、リトライ回数には制限があり、一定回数を超えた場合には、エラー報告、ログ記録、メールによるリマインダーの送信などの対策を講じる必要があります。

2 - 最初にデータベースを更新し、次にキャッシュを削除します (最適な解決策)

(1) スレッド A はデータベースを正常に更新します。
(2) スレッド B はキャッシュを正常に読み取ります。
(3) スレッド A はキャッシュを正常に削除します。
ここに画像の説明を挿入
最終キャッシュとデータベースのデータが一致しており、最新のデータであることがわかります。ただし、スレッド B はこのプロセス中に古いデータを読み取り、これら 2 つのステップの間にキャッシュ内の古いデータを読み取るスレッド B のような他のスレッドが存在する可能性がありますが、これらの 2 つのステップの実行速度は高速になるため、問題にはなりません。多くの。この 2 つの手順を実行すると、他のプロセスがキャッシュされたデータを読み込むときに、プロセス B のような問題は発生しなくなります。

メッセージキューを使用した削除の補償

また、データベースを更新してからキャッシュを削除する場合にも、データベースの更新は成功するが、キャッシュの削除中にエラーが発生して削除に失敗するなどの問題が発生します。このときキャッシュが再度読み取られるため、データは毎回間違っています。

現時点での解決策は、メッセージ キューを使用して削除を補うことです。具体的なビジネスロジックを言語で記述すると、
(1) 最初にスレッド A にデータベースの更新を要求する
(2) Redis の削除時にエラーが報告され、削除に失敗する
(3) このとき、Redis のキーとして使用されます メッセージ本文はメッセージ キューに送信されます;
(4) システムは、メッセージ キューによって送信されたメッセージを受信した後、Redis を再度削除します。

ここに画像の説明を挿入ただし、このソリューションには、ビジネス コードへの侵入が多くなり、相互に深く結合されるという欠点があるため、現時点では最適化方法が用意される予定です。Mysql データベースの更新操作後に、それを binlog ログに追加すると、対応する操作が見つかり、Mysql データベースの binlog ログをサブスクライブしてキャッシュを操作できます。

【4】まとめ

通常の状況では、キャッシュを更新するよりもキャッシュを削除する方が良い解決策です。最初にキャッシュを削除するよりも最初にデータベースを更新する方が良い解決策です。一般に、[最初にデータベースを更新してからキャッシュを削除する] は、4 つの方法のうち最も影響が少ないです。戦略、効果の最適解。

ただし、[先にキャッシュを削除してからデータベースを更新する]という解決策を使用する必要がある場合は、[遅延二重削除][読み書き分離時にメインライブラリを強制的に読み込む][リトライ機構]を使用して解決できます。問題。

[データベースを更新してからキャッシュを削除する]を使用した場合にキャッシュの削除に失敗する場合は、[Redisへのbinlog同期]を使用することで問題を解決できます。

おすすめ

転載: blog.csdn.net/weixin_44823875/article/details/129051198