blog

古いドライバを再生するためにあなたを取る:Redisの分散ロック、並行性の競争、一貫性のある二重書き込み

しかし、今すべての突然のAのネットワークのジッタは、その結果、Aはデータを書き込むためにRedisに行く最初のものではありません、操作の順序の全体のプロセスは、B -> C -> Aになっているので、...

Nov 7, 2020 · 5 min. read
シェア

前のレビュー

前回の記事を読んでいない学生は、まず前回の記事を読むことをお勧めします:

同時実行競争

この問題の根本的な原因は、同時書き込みであり、それ自体Redisは同時実行の問題ではない、前の記事を読んで学生が知っておくべき、Redisのスレッドモデルは、シングルスレッドであり、先入れ先出し操作の原則に絶対に準拠しているファイルイベントプロセッサキュー内のすべての操作命令は、ので、どのように同時競合の問題が発生するのですか?

例えば、A、B、Cの3つのクライアントがあり、A→B→Cの順でRedisにデータを書き込んだり更新したりする必要があるとします:

しかし、今、突然Aのネットワークのジッタ、その結果、AはRedisにデータを書き込むために最初に行くことはありません、操作の順序の全体のプロセスは、B→C→Aになっているので、データは間違っていません。

これは、あなたがオンラインで物を購入し、ショッピングカートを追加し、注文し、注文の順序がオフに変更された支払い、あなたが最初に注文し、次に支払い、ショッピングカートを追加し、このプロセスが続かないようなものです。このようなことがオンラインシステム上で起こると非常に怖いです。

このような状況はまた、解決することは非常に簡単ですが、このような分散ロックすることができます追加します:

Zookeeperは、同じ時間を確保するために、グローバルな分散ロックの実装に基づいてすることができ、唯一のキーの操作のシステムインスタンスが存在することができ、他の誰も読み書きすることはできません。

あなたは、キャッシュデータを書き込むには、DBからチェックアウトされ、DBに書き込む必要があります、DBに書き込まれたタイムスタンプを保存する必要があります、DBから時間をチェックアウトするには、タイムスタンプもチェックアウトされます。

書き込むたびに、まず現在の値のタイムスタンプがキャッシュ内の値のタイムスタンプより新しいかどうかを判断します。そうでなければ、新しいデータを古いデータで上書きすることはできません。

バイライト一貫性

キャッシュを使用する限り、キャッシュとデータベースの間で二重書き込みが発生する可能性があり、二重書き込みが発生する限り、データの一貫性に問題が発生します。

古典的なRedis + BDの読み書きモデルから始めましょう:

  • 読み込みの場合、まずキャッシュを読み込み、キャッシュがなければデータベースを読み込み、データを取り出してキャッシュに入れ、同時にレスポンスを返します。
  • 更新時には、まずデータベースを更新し、次にキャッシュを削除します。

なぜ更新せずにキャッシュを削除するのですか?

なぜキャッシュを更新せずに削除するのでしょうか?

理由は簡単で、より複雑なキャッシュのシナリオでは、キャッシュはデータベースから直接取得した値以上のものであることが多いからです。

たとえば、あるテーブルのフィールドを更新した場合、対応するキャッシュに他の2つのテーブルからデータを照会、キャッシュ内の最新の値を計算する処理を実行する必要があります。

また、キャッシュの更新は非常にコストがかかることがあります。データベースが変更されるたびに、対応するキャッシュのコピーを更新しなければならないということですか?そのようなシナリオもあるかもしれませんが、キャッシュされたデータを計算するような複雑なシナリオでは、そのようなことはありません。キャッシュに含まれる複数のテーブルを頻繁に変更する場合、キャッシュも頻繁に更新されます。しかし問題は、このキャッシュが頻繁にアクセスされるかどうかです。

たとえば、キャッシュに含まれるテーブルのフィールドが、わずか1分間に20回または100回変更された場合、キャッシュは20回または100回更新されますが、キャッシュは1分間に1回しか読み込まれず、大量のコールドデータが存在します。実際、キャッシュを削除すれば、キャッシュは1分間に1回再計算されるだけなので、オーバーヘッドは大幅に削減されます。キャッシュを使用する場合のみキャッシュをカウントしてください。

実際、キャッシュを更新するのではなく削除するのは、遅延計算のアイデアです。 使用されるかどうかに関係なく、毎回複雑な計算をやり直すのではなく、使用する必要があるときに再計算させればよいのです。mybatisやhibernateのように、遅延ロードのアイデアがあります。クエリ部門は、従業員のリストを持つ部門は、そこにあなたが部門をクエリするたびに、データ内のすべての1000人の従業員もああチェックアウトされると言う必要はありません。まず、部門をチェックすると同時に、内部の従業員にアクセスするには、この時間だけ、内部の従業員にアクセスしたいときに、1000人の従業員にクエリにデータベースに移動します。

厳密に "キャッシュ+データベース "が一致している必要があります!

このパターンは、現在ほとんどの人が使っているもので、「キャッシュ+データベース」は一貫していなければならないという厳密な要件がなくても問題ありません。

では、厳密な「キャッシュ+データベース」のシナリオでは、なぜ上記のシナリオが機能しないのでしょうか?

この問題は、データの読み書きが同時に行われた場合にのみ発生するからです。

つまり、同時性がそれほど高くない場合、例えば、キャッシュへの読み書きの要求が1sだけであれば、キャッシュ「キャッシュ+データベース」の不整合が発生しない確率が高いのです。

しかし、問題は、毎日のトラフィックが数億である場合、1秒あたりの同時読み取り数は数万であり、1秒あたりのデータ更新要求がある限り、上記の "キャッシュ+データベース "の不整合が発生する可能性があります。

だから、この問題に対する解決策はありませんが、もちろんありますが、厳密に "キャッシュ+データベース "は、シナリオの一貫性を維持する必要があります必要としない場合、それはこのプログラムを使用しないことをお勧めします。

あなたは、読み取り要求と書き込み要求をシリアライズすることができ、すべての読み取り要求と書き込み要求はキューにシリアライズされます。

シリアライズすることで、不整合はなくなりますが、システムのスループットが著しく低下し、ライン上のリクエストをサポートするために通常の数倍のマシンを使用することになります。

すべての操作をキューに入れると、確かに順序はカオスではありませんが、高い同時実行性、キューはブロックしやすいですが、システム全体のボトルネックになります。

あなたのスキャンコードの注意は、編集者がオリジナルを遵守するための最大の励みです :)
Read next

Javaアーキテクチャ - Javaコード仕様

コーディング標準に準拠したJava開発とは、一般的に以下の7点です。命名規約、コメント規約、インデント・組版規約、ファイル名規約、宣言規約、ステートメント規約、プログラミング規約。 すべての識別子は ASCII 文字、数字、アンダースコア "_" に限定されます。 一意なパッケージ名の接頭辞は、常にすべて小文字です。例:www....

Nov 6, 2020 · 3 min read