興味深い|データベースとキャッシュはどのようにして一貫性を確保しますか?

著者:Xiaolin Coding
グラフィカルコンピューターの基本Webサイト:https ://xiaolincoding.com/

ある日、上司は「最近、会社の利用者は増えているが、サーバーのアクセス速度はどんどん悪くなっている。アワンさんが最適化を手伝ってくれて、あなた!

写真

プログラマーのアワンは上司の口から「絵のケーキ」を聞くのをとても楽しみにしていて、上司からの仕事をためらうことなく受け入れました。

Awangがサーバーにログインし、調査の結果、サーバーのパフォーマンスのボトルネックがデータベースにあることが確認されました

これは簡単に実行できます。Redisをサーバーに追加して、データベースのキャッシュとして使用します。

このように、クライアントがデータを要求するときに、データがキャッシュでヒットする可能性がある場合、データベースにクエリを実行する代わりにキャッシュをクエリできるため、データベースへの負荷が軽減され、サーバーのパフォーマンスが向上します。

最初にデータベースを更新しますか、それとも最初にキャッシュを更新しますか?

Awangがこのアイデアを思いついた後、彼はサーバーの最適化を開始する準備ができていましたが、彼の前にはそのような問題がありました。

写真

キャッシュの導入により、データを更新する際には、データベースだけでなくキャッシュも更新する必要があります。これら2つの更新操作の前後に問題があります

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

Awangはあまり期待していませんでした。データベース内のデータを最新の状態に保つために、最新のデータを最初にデータベースを更新する必要があると感じたため、「最初にデータベースを更新し、次に、キャッシュを更新します」。

数晩投げた後、Awangはついに「サーバーを最適化」し、直接オンラインになり、自信を持って上司に報告するために走りました。

上司は技術を理解していないので、当然あまり心配しなかったので、ガワンにサーバーの状況を観察するように頼んだ。

Awangは数日間観察し、データベースへの負荷が大幅に軽減され、アクセス速度も大幅に向上したことを発見しました。

良い時代は長くは続かなかった。突然、上司が顧客から苦情を受けた。顧客は、年齢を更新するために2つの操作を開始したばかりだと言ったが、表示された年齢は確かに最初の更新の年齢であり、 2回目の更新は有効になりませんでした。

上司はすぐにNgawangを見つけ、Ngawangを叱責しました。「このような単純な更新操作にはバグがありますか?どこに顔を置きますか?それでもケーキが欲しいですか?

アワンは、手に入れようとしているケーキがなくなったと聞いてパニックになり、すぐにサーバーにログインして問題のトラブルシューティングを行いました。アワンは、キャッシュとデータベースのデータを照会した後、問題を発見しました。

データベースのデータはクライアントの2回目の更新操作のデータであり、キャッシュは実際に1回目の更新操作のデータです。つまり、データベースとキャッシュされたデータの間に不整合の問題があります

この問題は大きな問題です。一連の分析の後、Awangは同時実行の問題のために、キャッシュ内のデータとデータベースの間に不整合を引き起こしました。

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

たとえば、「リクエストA」と「リクエストB」の2つのリクエストが同時に「同じ」データを更新する場合、次のシーケンスが表示されることがあります。

写真

Aは最初にデータベースデータを1に更新するように要求し、次にBにデータベースデータを2に更新するように要求してからキャッシュを更新し、次にキャッシュも2に更新し、次にAがキャッシュを1に更新するように要求します。

このとき、データベースのデータは2ですが、キャッシュのデータは1であり、キャッシュとデータベースのデータに一貫性がありません

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

「最初にキャッシュを更新してからデータベースを更新する」という解決策にはまだ問題がありますか?

並行性の問題はまだあり、分析についても同じことが言えます。

「リクエストA」と「リクエストB」の2つのリクエストが同時に「同じ」データを更新すると仮定すると、次のシーケンスが発生する可能性があります。

写真

キャッシュされたデータを最初に1に更新するように要求し、次にデータベースを更新する前に、Bがキャッシュされたデータを2に更新するように要求し、次にデータベースを2に更新するように要求し、次にAがデータベースデータを1に更新するように要求します。

このとき、データベース内のデータは1ですが、キャッシュ内のデータは2であり、キャッシュ内のデータとデータベースの間に不整合があります

したがって、「最初にデータベースを更新してからキャッシュを更新する」か「最初にキャッシュを更新してからデータベースを更新する」かにかかわらず、どちらのスキームにも同時実行の問題があります。2つのリクエストが同じデータを同時に更新する場合、キャッシュが存在する可能性があります。データベース内のデータとの不整合

最初にデータベースを更新しますか、それとも最初にキャッシュを削除しますか?

問題を特定した後、Awangは、データを更新するときにキャッシュを更新するのではなく、キャッシュ内のデータを削除することにしました。次に、データを読み取ると、キャッシュにデータがないことがわかり、データベースからデータが読み取られてキャッシュに更新されます。

Awangが考える戦略には名前があり、それはキャッシュアサイド戦略と呼ばれ、中国人はバイパスキャッシュ戦略と呼ばれます。

この戦略は、さらに「読み取り戦略」と「書き込み戦略」に分類できます。

写真

戦略を書くためのステップ:

  • データベース内のデータを更新します。
  • キャッシュ内のデータを削除します。

戦略を読む手順:

  • 読み取られたデータがキャッシュにヒットした場合、データは直接返されます。
  • 読み取ったデータがキャッシュにヒットしない場合は、データベースからデータを読み取り、データをキャッシュに書き込んで、ユーザーに返します。

Nga Wangが「作文戦略」を考えたとき、彼はより深い思考に陥りました。彼はどちらの順序を選ぶべきですか?

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

前回のレッスンの後、今回は上司が大きなケーキをくれたので、ガワンはランダム選挙計画で「当然のこととは思わない」ので、それを把握する必要があります。

そのため、Awangは並行性の観点からそれを分析し、2つのスキームのどちらがデータベースとキャッシュ間のデータの一貫性を確保できるかを確認します。

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

Awangはまだユーザーテーブルのシーンを分析しています。

ユーザーの年齢が20歳であるとすると、リクエストAはユーザーの年齢を21歳に更新したいので、キャッシュ内のコンテンツを削除します。このとき、別のリクエストBがユーザーの年齢を読み取りたいと考えています。キャッシュにクエリを実行してミスを見つけた後、データベースから20歳を読み取り、キャッシュに書き込んでから、Aに続行をリクエストします。データベースを変更してユーザーを配置するには年齢が21に更新されました。

写真

最終的に、ユーザーの年齢はキャッシュで20(古い値)、データベースで21(新しい値)であり、キャッシュとデータベースのデータに一貫性がありません。

キャッシュを最初に削除してからデータベースを更新すると、「読み取りと書き込み」が同時に行われている場合でも、キャッシュとデータベース間のデータの不整合の問題が発生することがわかります。

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

「読み取り+書き込み」要求の同時シナリオの分析を続けます。

ユーザーデータがキャッシュに存在しない場合は、データを読み取るときにデータベースにクエリを実行して20歳を検索するように要求し、キャッシュに書き込まれていないときにデータを更新するように別の要求Bに要求します。データベースの経過時間を21に更新し、キャッシュをクリアします。このとき、リクエストAは、データベースから読み取った20歳のデータをキャッシュに書き込みます。

写真

最終的に、ユーザーの年齢はキャッシュで20(古い値)、データベースで21(新しい値)であり、キャッシュとデータベースのデータに一貫性がありません。

上記の理論的分析から、最初にデータベースを更新してからキャッシュを削除すると、データの不整合の問題も発生しますが、実際には、この問題の可能性は高くありません

キャッシュへの書き込みは通常、データベースへの書き込みよりもはるかに高速であるため、実際には、要求Bがデータベースを更新してキャッシュを削除した後で、Aにキャッシュの更新を要求することは困難です。

また、リクエストAがキャッシュを更新してからリクエストBがキャッシュを削除すると、キャッシュミスが原因で後続のリクエストがデータベースからデータを再読み込みするため、このような不整合は発生しません。

したがって、「最初にデータベースを更新してからキャッシュを削除する」というソリューションは、データの一貫性を確保できます

さらに、絶対確実にするために、Awangはキャッシュされたデータに「有効期限」も追加しました。この期間中にキャッシュされたデータに不整合があったとしても、最終的な一貫性を得るために有効期限があります。達成することができます。

このステップを考えた後、アワンは自分が本当に少し天才だと感じました。実際に「シームレス」な計画を考えていたので、一言も言わずにこの計画を採用し、数日投げた後、ようやく完成しました。

彼は自信を持って上司に最後の顧客の苦情を解決したことを報告しました。上司はガワンはいい人だと思っていたので、すぐに問題を解決してから、ガワンに数日間観察させました。

どうすればこんなにスムーズに進むことができますか?その結果、上司がデータを明確に更新したという顧客からの苦情を受け取るのにそれほど時間はかかりませんでしたが、データが有効になるまでに時間がかかり、顧客はそれを受け入れることができませんでした。

上司は顔が真っ白なアワンを探し、アワンにできるだけ早く問題を見つけてほしいと頼んだ。

別のバグがあることを知ったアワンは、さらにパニックに陥り、すぐにサーバーにログインして問題のトラブルシューティングを行いました。ログを確認した後、理由を知りました。

「最初にデータベースを更新してからキャッシュを削除する」は、実際には2つの操作です。これまでの分析はすべて、これら2つの操作を同時に正常に実行できるという事実に基づいています。この顧客からの苦情の問題は、キャッシュが削除されることです。 at ****(2番目の操作)が失敗したため、キャッシュ内のデータが古い値になりました

幸い、以前に有効期限がキャッシュに追加されていたため、一定期間後に更新が有効になるとお客様から言われる現象が発生しました。有効期限などがないと仮定すると、以降のリクエストは常にキャッシュを読み込む古いデータなので、問題はさらに大きくなります。

では、 「最初にデータベースを更新してからキャッシュを削除する」という2つの操作を確実に実行できるようにする方法について、新しい質問があります。

問題を分析した後、アワンはパニック状態で上司に問題を報告しました。

上司はそのことを知った後、問題を解決するためにガワンにさらに数日を与えました、そしてケーキを塗る問題は今回再び言及されませんでした。

Awangはこの問題をどのように解決しますか?

上司が描いたケーキはガワンに叶えることができますか?

未来を予測し、次回はガワンの話を聞いてください。

写真

まとめ

ガワンは以上です。他のことについて話しましょう。

「最初にデータベースを更新してからキャッシュを削除する」というソリューションは、データベースとキャッシュ間のデータの一貫性を保証しますが、データが更新されるたびに、キャッシュされたデータが削除され、キャッシュのヒット率に影響します。

したがって、当社のビジネスでキャッシュヒット率に対する要件が高い場合は、「データベースの更新+キャッシュの更新」ソリューションを採用できます。これは、キャッシュを更新してもキャッシュミスが発生しないためです。

ただし、以前にもこのソリューションを分析しました。2つの更新要求を同時に実行すると、データベースの更新とキャッシュの更新の2つの操作が独立しており、同時実行制御を行わないため、データの不整合の問題が発生します。操作。その後、2つのスレッドがそれらを同時に更新すると、書き込み順序の違いによってデータの不整合が発生します。

したがって、この問題を解決するためのいくつかの手段を追加する必要があります。ここに2つのアプローチがあります。

  • キャッシュを更新する前に、分散ロックを追加して、キャッシュを更新する1つのリクエストのみが同時に実行され、同時実行の問題が発生しないようにします。もちろん、ロックが導入された後は、書き込みのパフォーマンスに影響します。 。
  • キャッシュが更新されると、短い有効期限がキャッシュに追加されるため、キャッシュに一貫性がない場合でも、キャッシュされたデータはすぐに期限切れになり、ビジネスに受け入れられます。

ちなみに、「最初にキャッシュを削除してからデータベースを削除する」スキームの同時「読み取り+書き込み」要求によって引き起こされるキャッシュの不整合の解決策は、「遅延二重削除」です。

遅延二重削除実装の擬似コードは次のとおりです。

#删除缓存
redis.delKey(X)
#更新数据库
db.update(X)
#睡眠
Thread.sleep(N)
#再删除缓存
redis.delKey(X)

スリープ時間が追加されます。これは主に、リクエストAがスリープしているときに、リクエストBがこの期間中に「データベースからデータを読み取り、不足しているキャッシュをキャッシュに書き込む」操作を完了してから、Aに完了を要求できるようにするためです。スリープし、キャッシュを削除します。

したがって、リクエストAのスリープ時間は、リクエストBが「データベースからデータを読み取り+キャッシュに書き込む」時間よりも長くする必要があります。

ただし、特定のスリープ時間は実際には形而上学であり、評価が難しいため、このソリューションは可能な限り一貫性を確保することのみを目的としています。極端な場合でも、キャッシュの不整合が発生します。

したがって、「最初にデータベースを更新してから、キャッシュを削除する」ソリューションを使用することをお勧めします。


過去の状況レビュー

前回、プログラマーのAwangは、データアクセスのパフォーマンスを向上させるためにMySQLキャッシュレイヤーとしてRedisを導入しましたが、RedisとMySQLの間の二重書き込みの一貫性の問題を考慮する必要があるため、これはそれほど単純ではありません。

多くの挫折の後、Awangは最終的に「最初にデータベースを更新し、次にキャッシュを削除する」という戦略を選択しました。この戦略は、読み取りと書き込みを同時に行う場合でもデータの一貫性を最大化できるためです。

スマートなAwangは、キャッシュに有効期限を追加するボトムアップソリューションも考案しました。

このようにデータの整合性に問題はないと思ったので、機能起動後も上司から「データを明確に更新したが、データは有効になります」という苦情がありました。期間」、そして顧客はそれを受け入れることができませんでした。

上司はアワンに、別のバグがあることを知ってさらにパニックになり、すぐにサーバーにログインして問題のトラブルシューティングを行ったと語った。ログを確認した後、彼はその理由を知った。

「最初にデータベースを更新してからキャッシュを削除する」は、実際には2つの操作です。今回のお客様からの苦情の問題は、キャッシュの削除(2番目の操作)が失敗し、キャッシュ内のデータが古い値になることです。データベースが最新の値である間

幸い、以前に有効期限がキャッシュに追加されていたため、一定期間後に更新が有効になるとお客様から言われる現象が発生しました。有効期限などがないと仮定すると、以降のリクエストは常にキャッシュを読み込む古いデータなので、問題はさらに大きくなります。

では、 「最初にデータベースを更新してからキャッシュを削除する」という2つの操作を確実に実行できるようにする方法について、新しい質問があります。

問題を分析した後、アワンはパニック状態で上司に問題を報告しました。

上司はそのことを知った後、問題を解決するためにガワンにさらに数日を与えました、そしてケーキを塗る問題は今回再び言及されませんでした。

  • Awangはこの問題をどのように解決しますか?
  • 上司が描いたケーキはガワンに叶えることができますか?

両方の操作を正常に実行できるようにするにはどうすればよいですか?

今回のユーザーの不満は、キャッシュの削除(2番目の操作)が失敗し、キャッシュの値が古いため、データベースが最新の値であるため、データベースとキャッシュされたデータ間の不整合の問題が発生したためです。デリケートなビジネスに影響を与えます。

たとえば、説明するために。

アプリケーションはデータXの値を1から2に更新する必要があります。最初にデータベースを正常に更新してから、Redisキャッシュ内のXのキャッシュを削除しますが、この操作は失敗します。この時点で、データベース内のXの新しい値は2で、Redisの値は2です。Xのキャッシュ値は1であり、データベースとキャッシュされたデータの間に不整合の問題があります。

写真

次に、データXにアクセスするための後続のリクエストがある場合、最初にRedisでクエリが実行されます。キャッシュは削除されないため、キャッシュにヒットしますが、古い値の1が読み取られます。

実際、データベースを最初に操作する場合でも、キャッシュを最初に操作する場合でも、2番目の操作が失敗する限り、データの整合性の問題が発生します。

問題の原因はわかっていますが、どのように解決しますか?2つの方法があります:

  • 再試行メカニズム。
  • MySQL binlogをサブスクライブしてから、キャッシュを操作します。

最初のものについて話しましょう。

再試行メカニズム

メッセージキューを導入し、2番目の操作(キャッシュの削除)で操作するデータをメッセージキューに追加し、コンシューマーにデータを操作させることができます。

  • アプリケーションがキャッシュの削除に失敗した場合、アプリケーションはメッセージキューからデータを再読み取りしてから、キャッシュを再度削除できます。これが再試行メカニズムです。もちろん、再試行が特定の回数を超えても失敗する場合は、ビジネスレイヤーにエラーメッセージを送信する必要があります。
  • キャッシュの削除が成功した場合は繰り返しの操作を避けるためにデータをメッセージキューから削除する必要があります。それ以外の場合は、再試行を続行します。

再試行メカニズムのプロセスを説明するために例を見てください。

写真

MySQL binlogをサブスクライブしてから、キャッシュを操作します

「最初にデータベースを更新してからキャッシュを削除する」という戦略の最初のステップは、データベースを更新することです。データベースが正常に更新されると、変更ログが生成され、binlogに記録されます。

したがって、binlogログをサブスクライブすることで操作する特定のデータを取得し、キャッシュの削除を実行できます。AlibabaのオープンソースのCanalミドルウェアは、この実装に基づいています。

Canalは、MySQLマスタースレーブレプリケーションのインタラクティブプロトコルをシミュレートし、MySQLスレーブノードになりすまして、MySQLマスターノードにダンプリクエストを送信します。MySQLはリクエストを受信すると、BinlogをCanalにプッシュし始めます。Canalが解析した後Binlogバイトストリーム、ダウンストリームプログラムサブスクリプション用に読み取り可能な構造化データに変換されます。

次の図は、運河の仕組みです。

写真

したがって、「最初にデータベースを更新してからキャッシュを削除する」戦略の2番目の操作を確実に実行できるようにする場合は、「メッセージキューを使用してキャッシュの削除を再試行する」または「MySQLにサブスクライブする」を使用できます。これらの2つの方法には共通の機能があり、すべて非同期操作キャッシュを使用します。

上司がケーキを作っています

Awangはメッセージキューに精通しているため、このユーザーの問題を解決するために「メッセージキューを使用してキャッシュの削除を再試行する」スキームを使用することにしました。

数日夜の運用を経てサーバーが完成し、すぐに上司に報告します。

上司はガワンにもう少し観察してもらい、問題がなければ中秋節で「ケーキ」の話をします。

時が経ち、中秋節が開催されます。この期間中、ユーザーのフィードバックデータに一貫性がないという問題はありませんでした。

上司は、今回はガワンのパフォーマンスがとても良く、ミスもなく、サーバーのアクセスパフォーマンスも向上したので、この超大型の月餅をガワンに送りました。このケーキは、まるで大きくて丸いのがわかります。あなたのコードは長くてもっと。

写真

ガ・ワンはこの月餅を見て笑わずにはいられず、上司が描いたケーキだとは思っていなかったので、とても大きなケーキでした。

上記の話は純粋に架空のものであり、偶然の一致があれば、それはあなた次第です。

おすすめ

転載: blog.csdn.net/qq_34827674/article/details/123866483