Redis の面接でよくある 20 の質問

Redis の面接でよくある 20 の質問

序文

Redis典型的な面接の質問を 20 個まとめました。皆さんのお役に立てれば幸いです。

1.Redisとは何ですか? 主に何に使用されますか?

Redis (英語の正式名はRemote Dictionary Server (リモート辞書サービス)) は、 ANSI C 言語で書かれたオープンソースで、ネットワークをサポートし、メモリベースで永続的なログタイプの Key-Value データベースであり、API を提供します。多言語で。

MySQL データベースとは異なり、Redis データはメモリに保存されます。読み取りおよび書き込み速度は非常に速く、1 秒あたり 100,000 回を超える読み取りおよび書き込み操作を処理できます。したがって、Redis はキャッシュに広く使用されており、また、分散ロックとしてもよく使用されます。さらに、Redis はトランザクション、永続性、LUA スクリプト、LRU 駆動のイベント、および複数のクラスター ソリューションをサポートします。

2. Redis の基本的なデータ構造タイプについて話す

ほとんどの友人が知っているように、Redis には次の 5 つの基本タイプがあります。

  • 文字列(文字列)
  • ハッシュ
  • リスト(リスト)
  • セット(コレクション)
  • zset (順序セット)

また、3 つの特別なデータ構造タイプもあります。

  • 地理空間
  • ハイパーログログ
  • ビットマップ

2.1 Redis の 5 つの基本データ型

文字列(文字列)

  • はじめに: 文字列は Redis の最も基本的なデータ構造タイプです。バイナリ セーフであり、画像やシリアル化されたオブジェクトを保存できます。最大ストレージ値は 512M です。
  • 簡単な使用例:set key valueなどget key
  • アプリケーション シナリオ: 共有セッション、分散ロック、カウンター、および電流制限。
  • 内部エンコーディングには 3 種類あります。int(8字节长整型)/embstr(小于等于39字节字符串)/raw(大于39个字节字符串)

C 言語の文字列がchar[]実装されていますが、Redis はSDS (シンプル ダイナミック ストリング)パッケージ化を使用しており、sds ソース コードは次のとおりです。

struct sdshdr{ unsigned int len; // 标记buf的长度 unsigned int free; //标记buf中未使用的元素个数 char buf[]; // 存放元素的坑 }

SDS の構造図は次のとおりです。

Redis はなぜSDS構造を選択し、ネイティブ C 言語char[]は良くないのでしょうか?

たとえば、SDS では、文字列の長さは O(1) の時間計算量で取得できますが、C 文字列の場合は文字列全体を走査する必要があり、時間計算量は O(n) です。

ハッシュ

  • はじめに: Redis では、ハッシュ タイプは v (値) 自体がキーと値のペア (kv) 構造であることを指します。
  • 簡単な使用例:hset key field valuehget key field
  • 内部エンコーディング: ziplist(压缩列表)hashtable(哈希表)
  • アプリケーションシナリオ: ユーザー情報のキャッシュなど。
  • : 開発に hgetall を使用する場合、ハッシュ要素が多いと Redis がブロックされる可能性があるため、hscan を使用できます。一部のフィールドのみを取得する場合は、hmget を使用することをお勧めします。

文字列型とハッシュ型の比較は次のとおりです。

リスト(リスト)

  • はじめに: リスト (リスト) タイプは、複数の順序付けされた文字列を格納するために使用され、リストには最大 2^32-1 個の要素を格納できます。
  • 簡単で実践的な例: lpush key value [value ...]lrange key start end
  • 内部エンコード: ziplist (圧縮リスト)、linkedlist (リンクリスト)
  • アプリケーションシナリオ: メッセージキュー、記事リスト、

リスト タイプの挿入とポップアップを理解するための図:

リスト アプリケーション シナリオは次のことを指します。

  • lpush+lpop=スタック(スタック)
  • lpush+rpop=キュー (キュー)
  • lpsh+ltrim=Capped Collection (限定コレクション)
  • lpush+brpop=Message Queue (メッセージキュー)

セット(コレクション)

  • はじめに: コレクション (セット) 型は複数の文字列要素を格納するためにも使用されますが、要素の重複は許可されません
  • 簡単な使用例:sadd key element [element ...]smembers key
  • 内部エンコーディング: intset(整数集合)hashtable(哈希表)
  • : smembers、lrange、および hgetall は比較的重いコマンドですが、Redis をブロックする可能性のある要素が多すぎる場合は、sscan を使用して完了できます。
  • 応用シナリオ:ユーザータグ、乱数宝くじの生成、ソーシャルニーズ。

順序付きセット (zset)

  • はじめに: ソートされた文字列のコレクション。要素は繰り返すことができません。
  • 簡単な形式の例: zadd key score member [score member ...]zrank key member
  • 低レベルの内部エンコード: ziplist(压缩列表)skiplist(跳跃表)
  • アプリケーション シナリオ: リーダーボード、ソーシャル ニーズ (ユーザーの好みなど)。

2.2 Redis の 3 つの特殊なデータ型

  • 地理: Redis 3.2 によって導入された地理位置情報は、地理的位置情報を保存し、保存された情報を操作するために使用されます。
  • HyperLogLog: 統計 Web サイトの UV など、カーディナリティ統計アルゴリズムに使用されるデータ構造。
  • ビットマップ: ビットを使用して要素の状態をマップします。Redis では、その最下位層が文字列型に基づいて実装され、ビットマップをビット単位で配列にすることができます。

3. Redis はなぜそれほど速いのですか?

Redis が非常に速い理由

3.1 メモリストレージに基づいた実装

メモリの読み取りと書き込みがディスクよりもはるかに高速であることは誰もが知っていますが、メモリ ストレージに基づいて Redis によって実装されたデータベースは、データがディスクに保存される MySQL データベースと比較して、ディスク I/O の消費を節約します。

3.2 効率的なデータ構造

MySQL インデックスは効率を向上させるために B+ ツリーのデータ構造を選択することがわかっています。実際、合理的なデータ構造により、アプリケーション/プログラムを高速化できます。まず、Redis のデータ構造と内部コーディング図を見てください。

SDS 単純な動的文字列

  • 文字列の長さの処理: Redis では文字列の長さを取得し、計算量は O(1) ですが、C 言語では最初からたどる必要があり、計算量は O(n) です。
  • スペースの事前割り当て: 文字列が変更される頻度が高くなるほど、メモリ割り当てによるパフォーマンスの消費が増加しますが、SDS の変更とスペースの拡張により、パフォーマンスの損失を軽減するために追加の未使用スペースが割り当てられます。
  • 遅延スペース解放: SDS が短縮されると、余分なメモリ スペースを再利用する代わりに、free が余分なスペースを記録します。その後変更があった場合、free に記録されたスペースは割り当てを減らすために直接使用されます。
  • バイナリ セキュリティ: Redis はバイナリ データを保存できます。C 言語では、文字列は '\0' に遭遇すると終了しますが、SDS では、len 属性が文字列の終わりをマークします。

辞書

RedisはKVタイプのメモリデータベースであり、すべてのキー値は辞書に格納されます。ディクショナリは、HashMap などのハッシュ テーブルであり、キーを通じて対応する値を直接取得できます。ハッシュテーブルの特徴としては、O(1)の計算量で対応する値が得られる。

ジャンプ台

  • ジャンプテーブルはRedis独自のデータ構造で、リンクリストをベースにし、検索効率を高めるために多段のインデックスを追加しています。
  • ジャンプ テーブルは、平均 O(logN) および最悪 O(N) の複雑さのノード ルックアップをサポートし、順次操作を通じてノードをバッチ処理することもできます。

3.3 合理的なデータエンコーディング

Redis は複数のデータ型をサポートし、プリミティブ型ごとに、場合によっては複数のデータ構造をサポートします。いつ、どのようなデータ構造を使用するか、どのエンコーディングを使用するかは、Redis デザイナーの要約と最適化の結果です。

  • 文字列: 数値を格納する場合は、int 型のエンコーディングを使用します。数値以外の 39 バイト以下の文字列を格納する場合は、embstr を使用します。39 バイトを超える場合は、raw エンコーディングを使用します。
  • リスト: リスト内の要素の数が 512 未満で、リスト内の各要素の値が 64 バイト未満 (デフォルト) の場合は、ziplist エンコーディングを使用し、それ以外の場合はリンクリスト エンコーディングを使用します。
  • ハッシュ: ハッシュ タイプの要素の数が 512 未満で、すべての値が 64 バイト未満の場合は、ziplist エンコーディングを使用し、それ以外の場合はハッシュテーブル エンコーディングを使用します。
  • セット: セット内の要素がすべて整数で、要素の数が 512 未満の場合は、intset エンコーディングを使用します。それ以外の場合は、ハッシュテーブル エンコーディングを使用します。
  • Zset: 順序付きセット内の要素の数が 128 未満で、各要素の値が 64 バイト未満の場合は、ziplist エンコーディングを使用します。それ以外の場合は、skiplist (スキップ リスト) エンコーディングを使用します。

3.4 合理的なスレッドモデル

I/O多重化

I/O多重化

複数の I/O 多重化テクノロジにより、単一のスレッドで複数の接続要求を効率的に処理できます。Redis は、I/O 多重化テクノロジの実装として epoll を使用します。さらに、Redis 独自のイベント処理モデルは、epoll での接続、読み取りと書き込み、およびシャットダウンをイベントに変換し、ネットワーク I/O に多くの時間を無駄にしないようにします。

I/O多重化とは何ですか?

  • I/O :网络 I/O
  • マルチウェイ: 複数のネットワーク接続
  • 多重化: 同じスレッドを多重化します。
  • IO 多重化は実際には同期 IO モデルであり、スレッドが複数のファイル ハンドルを監視できるようにします。ファイル ハンドルの準備が完了すると、対応する読み取りおよび書き込み操作を実行するようにアプリケーションに通知できます。ファイル ハンドルの準備ができていない場合は、アプリケーションを作成し、CPU を引き渡します。

シングルスレッドモデル

  • Redis はシングル スレッド モデルであり、シングル スレッドにより、CPU の不必要なコンテキスト切り替えや競合するロックの消費が回避されます。またシングルスレッドのため、コマンド(hgetallコマンドなど)を長時間実行するとブロッキングが発生します。Redis は、高速実行シナリオのためのデータベースです。したがって、smembers、lrange、hgetall などのコマンドは注意して使用してください。
  • Redis 6.0 ではマルチスレッドの高速化が導入されていますが、その実行コマンド操作メモリは依然としてシングルスレッドです。

3.5 仮想メモリの仕組み

RedisはVM機構を自ら直接構築するため、一般的なシステムのようにシステム関数を呼び出して処理することがないため、移動やリクエストにある程度の時間がかかります。

Redis の仮想メモリの仕組みとは何ですか?

仮想メモリ メカニズムは、アクセス頻度の低いデータ (コールド データ) をメモリからディスクに一時的にスワップし、アクセスする必要がある他のデータ (ホット データ) のために貴重なメモリ領域を解放します。ホット データとコールド データは VM 機能によって分離できるため、ホット データはメモリ内に残り、コールド データはディスクに保存されます。これにより、メモリ不足によるアクセス速度の低下の問題を回避できます。

4. キャッシュ ブレークダウン、キャッシュ ペネトレーション、キャッシュ アバランチとは何ですか?

4.1 キャッシュ侵入の問題

まず、キャッシュの一般的な使用方法を見てみましょう。読み取りリクエストが来ると、まずキャッシュをチェックし、キャッシュに値がヒットした場合は直接戻ります。キャッシュがヒットしなかった場合は、データベースをチェックします。次に、データベース値をキャッシュに更新してから戻ります。

読み取りキャッシュ

キャッシュペネトレーション: 存在してはいけないデータのクエリを指します。キャッシュはミスであるため、データベースからクエリする必要があります。データが見つからない場合、データはキャッシュに書き込まれません。これにより、クエリが要求されるたびに、存在しないデータがデータベースに送信されるため、データベースに負荷がかかります。

平たく言えば、読み取りリクエストがアクセスされるとき、キャッシュにもデータベースにも特定の値がないため、この値に対するすべてのクエリリクエストがデータベースに浸透します。これがキャッシュ浸透です。

キャッシュの侵入は通常、次の状況で発生します。

  • ビジネスの不合理な設計。たとえば、ほとんどのユーザーはガードを有効にしていませんが、特定のユーザー ID がガードされているかどうかを確認するために各リクエストがキャッシュされます。
  • キャッシュやデータベースのデータを誤って削除するなど、業務・運用や保守・開発のミス。
  • ハッカーは違法に攻撃を要求します。たとえば、ハッカーは、存在しないビジネス データを読み取るための多数の違法な要求を意図的にでっち上げます。

キャッシュの侵入を回避するにはどうすればよいでしょうか? 大きく分けて3つの方法があります。

  • 1. 不正なリクエストの場合は、API エントリのパラメータを検証し、不正な値をフィルタリングします。
  • 2. クエリ データベースが空の場合は、キャッシュに null 値またはデフォルト値を設定できます。ただし、書き込み要求が来た場合は、キャッシュの整合性を確保するためにキャッシュを更新する必要があり、同時に、最終的にキャッシュに適切な有効期限が設定されます。(ビジネスでよく使われる、シンプルで効果的)
  • 3. ブルーム フィルターを使用して、データが存在するかどうかをすばやく判断します。つまり、クエリリクエストが来ると、まずブルームフィルターで値が存在するかどうかを判断し、その後、値が存在するかどうかを確認し続けます。

ブルーム フィルターの原理: 初期値 0 のビットマップ配列と N 個のハッシュ関数で構成されます。キーに対して N 個のハッシュ アルゴリズムを実行して N 個の値を取得し、ビット配列内の N 個の値をハッシュして 1 に設定し、特定の位置がすべて 1 であるかどうかを確認して、ブルーム フィルター デバイスがキーが存在すると判断します。 。

4.2 キャッシュスケジュールの問題

キャッシュ スノーラン:有効期限までのキャッシュ内の大量のデータを指しますが、クエリ データの量が膨大で、すべてのリクエストがデータベースに直接アクセスするため、データベースに過剰な負荷がかかり、場合によってはマシンがダウンすることがあります。

  • Xueben のキャッシュは、一般に大量のデータの有効期限が同時に切れることによって発生するため、有効期限を均等に設定する、つまり有効期限を比較的離散的に設定することで解決できます。たとえば、より大きな固定値 + より小さなランダム値、5 時間 + 0 ~ 1800 秒を使用します。
  • Redis のダウンタイムもキャッシュのスノーランニングを引き起こす可能性があります。これには、Redis 高可用性クラスターの構築が必要です。

4.3 キャッシュの故障問題

キャッシュの内訳:ホットスポット キーが特定の時点で期限切れになり、この時点でこのキーに対する同時リクエストが多数あるため、大量のリクエストがデータベースにヒットすることを指します。

キャッシュ ブレークダウンは少し似ていますが、この 2 つの違いは、キャッシュ パンクチャはデータベースに過大な負荷がかかるか、場合によってはダウンすることを意味するのに対し、キャッシュ ブレークダウンは DB データベース レベルへの同時リクエストの数が単なる多数であることです。この内訳は、キャッシュされた Xueben のサブセットであると考えることができます。一部の記事では、この 2 つの違いは、内訳が特定のホット キー キャッシュに関するものであるのに対し、Xueben は多くのキーに関するものであると考えられています。

解決策は 2 つあります。

  • 1. ミューテックス ロック スキームを使用しますキャッシュが失敗した場合は、DB データをすぐにロードするのではなく、(Redis の setnx) など、正常に返されるアトミック操作コマンドを使用して操作し、DB データベース データをロードして、成功したときにキャッシュを設定します。それ以外の場合は、キャッシュの取得を再試行します。
  • 2. 「有効期限なし」とは、有効期限が設定されていないことを意味しますが、ホットスポット データの有効期限が近づくと、非同期スレッドが更新され、有効期限が設定されます。

5. ホットキーの問題とは何ですか、ホットキーの問題を解決する方法

ホットキーとは何ですか? Redis では、アクセス頻度が高いキーをホットキーと呼びます。

ホット キーの要求がサーバー ホストに到達すると、要求の量が非常に多くなるため、ホストのリソースが不足したり、クラッシュしたりして、通常のサービスに影響を与える可能性があります。

そして、ホットキーはどのように生成されるのでしょうか? 主な理由は 2 つあります。

  • ユーザーが消費するデータは、フラッシュセールや話題のニュースなど、読み取りが多く書き込みが少ないシナリオなど、生成されるデータよりもはるかに大きくなります。
  • 集中リクエストのフラグメンテーションは単一の Redi サーバーのパフォーマンスを超え、たとえば、固定名のキーとハッシュが同じサーバーに分類されるため、瞬間的なアクセス量が非常に大きくなり、マシンのボトルネックを超えてホット キーの問題が発生します。

では、日々の開発において、ホットキーを識別するにはどうすればよいでしょうか?

  • どれがホットキーであるかを経験に基づいて判断します。
  • クライアント統計レポート。
  • サービスプロキシ層レポート

ホットキーの問題を解決するにはどうすればよいですか?

  • Redis クラスターの拡張: 断片化されたコピーを増やし、読み取りトラフィックのバランスをとります。
  • ホットキーをさまざまなサーバーに配布します。
  • 2 次キャッシュ、つまり JVM ローカル キャッシュを使用して、Redis の読み取りリクエストを減らします。

6. Redis の有効期限戦略とメモリ削除戦略

6.1 Redis の有効期限戦略

ここでset key、たとえば有効期限を設定できますexpire key 60キーが 60 秒後に期限切れになるように指定します。60 秒後、redis はそれをどのように処理しますか? まず、いくつかの有効期限戦略を紹介します。

期限付き有効期限

有効期限のあるキーごとにタイマーを作成する必要があり、有効期限に達するとキーはすぐにクリアされます。この戦略では期限切れのデータをすぐにクリアできるため、メモリに優しいですが、期限切れのデータを処理するために多くの CPU リソースが消費されるため、キャッシュの応答時間とスループットに影響します。

遅延有効期限

キーにアクセスした場合のみ、キーの有効期限が切れているかどうかが判定され、有効期限が切れた場合はクリアされます。この戦略は CPU リソースを最大限に節約できますが、メモリには非常に優しくありません。極端な場合には、期限切れのキーが大量に再アクセスされなくなる可能性があるため、それらのキーはクリアされず、大量のメモリを占有します。

定期的に期限切れになる

一定の間隔で、特定の数のデータベースの期限切れディクショナリ内の特定の数のキーがスキャンされ、期限切れのキーがクリアされます。この戦略は、前者 2 つの間の妥協案です。スケジュールされたスキャンの時間間隔と各スキャンの限られた消費時間を調整することで、さまざまな状況で CPU とメモリのリソースのバランスを最適に取ることができます。

Expires ディクショナリは、有効期限が設定されたすべてのキーの有効期限データを保存します。ここで、キーはキー空間内のキーへのポインタであり、値はキーのミリ秒精度の UNIX タイムスタンプで表される有効期限です。 。キースペースは、Redis クラスターに保存されているすべてのキーを指します。

Redis では、遅延有効期限と定期有効期限という2 つの有効期限戦略が同時に使用されます。

  • Redis が現在 300,000 個のキーを保存しており、それらすべてに有効期限が設定されていると仮定すると、100 ミリ秒ごとにすべてのキーをチェックすると、CPU の負荷が非常に高くなり、最終的にはハングアップする可能性があります。
  • したがって、redis は定期的な有効期限を採用し、100 ミリ秒ごとに特定の数のキーをランダムに選択してチェックおよび削除します。
  • ただし、最終的には、削除されずに期限切れのキーが多数存在する可能性があります。現時点では、redis は遅延削除を採用しています。キーを取得すると、redis がそれをチェックします。キーに有効期限が設定されており、有効期限が切れている場合は、この時点で削除されます。

ただし、通常の削除で期限切れのキーを大量に見逃す場合は、遅延削除を使用しません。メモリには期限切れのキーが大量に蓄積され、メモリの爆発に直接つながります。あるいは、業務量が増加すると、Redis キーが頻繁に使用され、単純にメモリが不足し、運用保守担当者もメモリを増やすのを忘れてしまうこともあります。Redis はこのままハングアップしてしまうのでしょうか? しない!Redis は 8 つのメモリ除去戦略で自身を保護します~

6.2 Redis メモリの削除戦略

  • volatile-lru: メモリが新しく書き込まれたデータを収容するのに十分でない場合は、LRU (最も最近使用されていない) アルゴリズムを使用して、有効期限が設定されたキーから削除します。
  • allkeys-lru: メモリが新しく書き込まれたデータを収容するのに十分でない場合は、LRU (最も最近使用されていない) アルゴリズムを使用してすべてのキーから削除します。
  • volatile-lfu: バージョン 4.0 で追加されました。メモリが新しく書き込まれたデータを収容するのに十分でない場合、LFU アルゴリズムを使用して期限切れのキー内のキーが削除されます。
  • allkeys-lfu: バージョン 4.0 の新機能。メモリが新しく書き込まれたデータを収容するのに十分でない場合、LFU アルゴリズムを使用してすべてのキーが削除されます。
  • volatile-random: メモリが新しく書き込まれたデータを収容するのに十分でない場合、有効期限が設定されたデータはキーからランダムに削除されます。
  • allkeys-random: メモリが新しく書き込まれたデータを収容するのに十分でない場合、データはすべてのキーからランダムに削除されます。
  • volatile-ttl: 新しく書き込まれたデータを格納するのにメモリが十分でない場合、有効期限が設定されたキーでは、有効期限に応じて削除され、有効期限の早い方が最初に削除されます。
  • noeviction: デフォルトのポリシーでは、メモリが新しく書き込まれたデータを収容するのに十分でない場合、新しい書き込み操作でエラーが報告されます。

7. Redis の一般的なアプリケーション シナリオについて話す

  • キャッシュ
  • リーダーボード
  • カウンターアプリケーション
  • 共有セッション
  • 分散ロック
  • ソーシャルネットワーク
  • メッセージキュー
  • ビット操作

7.1 キャッシュ

Redis というと当然キャッシュが思い浮かびますが、国内外の中規模および大規模 Web サイトはキャッシュと切っても切れない関係にあります。ホットデータをキャッシュするなど、キャッシュを適切に使用すると、Web サイトのアクセス速度が向上するだけでなく、データベース DB への負荷も軽減されます。さらに、memcached と比較して、Redis は豊富なデータ構造も提供し、強力なバッチである RDB や AOF などの永続化メカニズムも提供します。

7.2 リーダーボード

現在のインターネットアプリケーションには、ECサイトの月次売上ランキング、ソーシャルAPPのギフトランキング、小規模プログラムの投票ランキングなど、さまざまなランキングが存在します。Redis が提供するデータ型によりzset、これらの複雑なリーダーボードが可能になります。

たとえば、ユーザーが毎日ビデオをアップロードして「いいね!」を獲得した場合、リーダーボードは次のように設計できます。

  • 1. ユーザー Jay がビデオをアップロードすると、「いいね」を 6 件獲得できます。

zadd user:ranking:2021-03-03 Jay 3

  • 2. 一定の時間が経過したら、次の「いいね!」を取得します。次のようにすることができます。

zincrby user:ranking:2021-03-03 Jay 1

  • 3. ユーザー John が不正行為をした場合、ユーザーを削除する必要があります。

zrem user:ranking:2021-03-03 John

  • 4.「いいね!」の多いユーザー3名を表示

zrevrangebyrank user:ranking:2021-03-03 0 2

7.3 カウンターの適用

大手のWebサイトやAPPアプリでは、短い動画の再生数やECサイトの閲覧数などのカウンター機能が必要となることが多いです。これらの再生数と閲覧数は通常リアルタイムである必要があり、再生と閲覧ごとに 1 ずつ増加する必要があります。同時実行の量が多い場合、従来のリレーショナル データのパフォーマンスが困難になります。Redis は当然カウント機能をサポートしており、カウントパフォーマンスも非常に優れているため、カウンターシステムにとって重要な選択肢と言えます。

7.4 共有セッション

分散 Web サービスがユーザーのセッション情報を独自のサーバーに保存する場合、ユーザーは更新後に再度ログインする必要がある可能性があり、これは明らかに問題です。実際、Redis を使用してユーザーのセッションを一元管理することができ、ユーザーがログイン情報を更新またはクエリするたびに、情報は Redis から一元的に直接取得されます。

7.5 分散ロック

ほぼすべてのインターネット企業は分散展開を使用していますが、分散サービスでは、フラッシュ キル、注文の削減、在庫の削減など、同じリソースへの同時アクセスにおいて技術的な問題が発生します。

  • ローカル ロックの同期や再入ロックを使用することは絶対に不可能です。
  • 同時実行の量が大きくない場合は、データベースの悲観的ロックと楽観的ロックを使用して同時実行を実現しても問題ありません。
  • ただし、同時実行性が高い場合、データベース ロックを使用してリソースへの同時アクセスを制御すると、データベースのパフォーマンスに影響します。
  • 実際、Redis の setnx を使用して分散ロックを実装できます。

7.6 ソーシャルネットワーキング

好き/嫌い、ファン、共通の友人/お気に入り、プッシュ、プルダウンの更新などは、ソーシャル ネットワーキング サイトの重要な機能です。ソーシャル ネットワーキング サイトは通常、大量のアクセスがあり、従来のリレーショナル データはこれを保存するのには適していません。データの種類、Redis 提供されるデータ構造により、これらの機能を比較的簡単に実現できます。

7.7 メッセージキュー

メッセージ キューは、ActiveMQ、RabbitMQ、Kafka やその他の一般的なメッセージ キュー ミドルウェアなど、大規模な Web サイトで必須のミドルウェアです。これらは主にビジネスの分離、トラフィックのピークカット、およびリアルタイム性の低いビジネスの非同期処理に使用されます。Redis は、単純なメッセージ キュー システムを実装できるパブリッシュ/サブスクライブおよびブロック キュー機能を提供します。さらに、これはプロフェッショナルなメッセージ ミドルウェアと比較することはできません。

7.8 ビット操作

これは、数億人のユーザーのシステム サインイン、重複排除されたログイン数のカウント、ユーザーがオンラインかどうかなど、データ量が数億件になるシナリオで使用されます。Tencent には 10 億人のユーザーがいるのに、特定のユーザーがオンラインであるかどうかを数ミリ秒以内に確認するにはどうすればよいでしょうか? ユーザーごとにキーを作成して、それらを 1 つずつ記録するなどとは言わないでください (必要なメモリが膨大になることを計算できますし、同様の要件が多数あります。ここではビット操作を使用する必要があります。setbit、getbit、 bitcount コマンドの原理は次のとおりです: Redis で十分な長さの配列を構築し、各配列要素は 0 と 1 の 2 つの値のみを持つことができ、この配列の添字インデックスはユーザー ID (数値である必要があります) を表すために使用されます。 ) であれば、明らかに、この長さ数億の大きな配列は、添字と要素値 (0 と 1) を通じてメモリ システムを構築できます。

8. Redis の永続化メカニズムは何ですか? メリットとデメリットについて話す

Redis はメモリベースの非リレーショナル KV データベースですが、メモリベースであるため、Redis サーバーがハングするとデータが失われます。データの損失を避けるために、Redis はデータをディスクに保存する永続性を提供します。

Redis は、 RDB と AOF という2 つの永続化メカニズムを提供しており、その永続的なファイルの読み込みプロセスは次のとおりです。

8.1 RDB

RDBはメモリデータをスナップショットの形でディスクに保存します。

スナップショットとは何かというと、このように理解すると、現時点でのデータを写真に撮って保存するものです。

RDB の永続化とは、指定された時間間隔内に指定された回数の書き込み操作を実行し、メモリ内のデータセットのスナップショットをディスクに書き込むことを指します。これは Redis のデフォルトの永続化方法です。操作が完了すると、指定したディレクトリにファイルが生成されdump.rdb、Redis再起動時にdump.rdbそのファイルをロードすることでデータが復元されます。RDB トリガー メカニズムには主に次のようなものがあります。

RDBのメリット

  • バックアップ、フルコピーなどの大規模なデータ回復シナリオに適しています。

RDBのデメリット

  • リアルタイムの永続性/第 2 レベルの永続性を実現する方法はありません。
  • 新旧バージョンには RDB フォーマットの互換性の問題があります

AOF

AOF (ファイル追加のみ)永続化では、ログの形式を使用して各書き込み操作を記録し、それをファイルに追加し、再起動時に AOF ファイル内のコマンドを再実行してデータを復元します。これは主にデータの永続性に関するリアルタイムの問題を解決します。デフォルトでは有効になっていません。

AOF のワークフローは次のとおりです。

AOFのメリット

  • データの一貫性と完全性の向上

AOFのデメリット

  • AOF レコードが多いほどファイルが大きくなり、データの回復が遅くなります。

9. Redis の高可用性を実現するにはどうすればよいですか?

私たちはプロジェクトで Redis を使用していますが、Redis サービスの単一のデプロイメント ポイントではありません。シングルポイント展開が停止すると、利用できなくなるためです。高可用性を実現するには、データベースの複数のコピーを複製して異なるサーバーに展開するのが一般的で、そのうちの 1 つがダウンしてもサービスは継続して提供されます。Redis が高可用性を実現するには、マスター/スレーブ モード、センチネル モード、クラスター モードの3 つのデプロイメント モードがあります

9.1 マスタースレーブモード

マスター/スレーブ モードでは、Redis は複数のマシンをデプロイします。マスター ノードは読み取りおよび書き込み操作を担当し、スレーブ ノードは読み取り操作のみを担当します。スレーブ ノードのデータはマスター ノードから取得され、その実現原理はマスター/スレーブ レプリケーション メカニズムです。

マスター/スレーブ レプリケーションには、完全レプリケーションと増分レプリケーションが含まれます。一般に、スレーブがマスターに初めて接続を開始するとき、またはそれが最初の接続であると考えられるとき、フル コピーが使用されます。フル コピー プロセスは次のとおりです。

  • 1. スレーブはマスターに同期コマンドを送信します。
  • 2. マスターは SYNC コマンドを受信した後、bgsave コマンドを実行して完全な RDB ファイルを生成します。
  • 3. マスターはバッファを使用して、RDB スナップショットの生成中にすべての書き込みコマンドを記録します。
  • 4. マスターが bgsave を実行した後、RDB スナップショット ファイルをすべてのスレーブに送信します。
  • 5. スレーブは RDB スナップショット ファイルを受信すると、受信したスナップショットをロードして解析します。
  • 6. マスターはバッファを使用して、RDB 同期中に生成されたすべての書き込みコマンドを記録します。
  • 7. マスター スナップショットが送信された後、バッファ内の書き込みコマンドをスレーブに送信し始めます。
  • 8.salveはコマンドリクエストを受け付け、マスターバッファから書き込みコマンドを実行します。

redis2.8 以降では、sync コマンドは多くのシステム リソースを消費し、 psync の方が効率的であるため、 syncの代わりに psync が使用されます。

スレーブがマスターと完全に同期した後、マスター上のデータが再度更新されると、増分レプリケーションがトリガーされます。

マスター ノードでデータが増加または減少すると、replicationFeedSalves()この関数がトリガーされ、マスター ノードで呼び出されるすべてのコマンドがreplicationFeedSlaves()スレーブ ノードとの同期に使用されます。本関数を実行する前に、マスタノードはユーザが実行したコマンドにデータ更新があるかどうかを判定し、データ更新があり、スレーブノードが空でない場合に本関数が実行されます。この関数の機能は、ユーザーが実行したコマンドをすべてのスレーブノードに送信し、スレーブノードに実行させることです。プロセスは次のとおりです。

9.2 セントリーモード

マスタースレーブモードでは、マスターノードが障害によりサービスを提供できなくなった場合、手動でスレーブノードをマスターノードに昇格させると同時に、アプリケーションにマスターノードのアドレスを更新するように通知する必要があります。明らかに、ほとんどのビジネス シナリオでは、この障害処理方法を受け入れることができません。Redis は、この問題を解決するために、2.8 以降、Redis Sentinel (センチネル) アーキテクチャを正式に提供しています。

Sentinel モードは、1 つ以上の Sentinel インスタンスで構成される Sentinel システムで、すべての Redis マスター ノードとスレーブ ノードを監視でき、監視対象のマスター ノードがオフラインになると、マスター サーバー配下の特定のノードが自動的にログオフされます。が新しいマスター ノードに昇格しますただし、センチネルプロセスがRedisノードを監視すると問題(シングルポイント問題)が発生する可能性があるため、複数のセンチネルを使用してRedisノードを監視し、各センチネル間で監視を行うことになります。

センチネル センチネルモード

簡単に言えば、センチネル モードには 3 つの機能があります。

  • コマンドを送信し、Redis サーバー (マスター サーバーとスレーブ サーバーを含む) が実行ステータスを監視するために戻るのを待ちます。
  • Sentinel はマスター ノードがダウンしていることを検出し、スレーブ ノードをマスター ノードに自動的に切り替えます。その後、パブリッシュ/サブスクライブ モードを通じて他のスレーブ ノードに通知し、構成ファイルを変更して、ホストを切り替えます。
  • Sentinel は、高可用性を実現するために相互監視も行います。

フェイルオーバープロセスはどのようなものですか?

メインサーバーがダウンしていると仮定すると、Sentinel 1 が最初にこの結果を検出し、システムがフェイルオーバー処理をすぐに実行するわけではなく、Sentinel 1 が主観的にメインサーバーが利用できないと判断しているだけであり、この現象は主観的なオフラインになります。後続のセントリーもメイン サーバーが利用できないことを検出し、その数が特定の値に達すると、セントリー間で投票が行われ、投票の結果に応じてセンチネルによってフェイルオーバー操作が開始されます。切り替えが成功すると、各センチネルは、パブリッシュ/サブスクライブ モデルを通じて、監視しているスレーブ サーバーからホストを切り替えます。このプロセスは、客観的オフラインと呼ばれます。このようにして、クライアントに対してすべてが透過的になります。

Sentinel の動作モードは次のとおりです。

  1. 各 Sentinel は、既知のマスター、スレーブ、および他の Sentinel インスタンスに 1 秒に 1 回 PING コマンドを送信します。
  2. インスタンス (インスタンス) が、PING コマンドに対する最後の有効な応答からの down-after-milliseconds オプションで指定された値よりも長い場合、そのインスタンスは Sentinel によって主観的にオフラインとしてマークされます。
  3. マスターが主観的にオフラインとしてマークされている場合、マスターを監視しているすべてのセンチネルは、マスターが実際に主観的にオフライン状態に入ったことを 1 秒に 1 回確認する必要があります。
  4. 十分な数のセンチネル (設定ファイルで指定された値以上) が、マスターが指定された時間範囲内で実際に主観的オフライン状態に入ったことを確認すると、マスターは客観的にオフラインとしてマークされます。
  5. 一般に、各センチネルは 10 秒ごとにすべての既知のマスターとスレーブに INFO コマンドを送信します。
  6. マスターが Sentinel によって客観的にオフラインとしてマークされると、Sentinel がオフライン マスターのすべてのスレーブに INFO コマンドを送信する頻度が 10 秒に 1 回から 1 秒に 1 回に変更されます。
  7. マスターがオフラインであることに同意するセンチネルが十分にいない場合、マスターの客観的なオフライン ステータスは削除され、マスターがセンチネルの PING コマンドに対して有効な応答を返した場合、マスターの主観的なオフライン ステータスは削除されます。

9.3 クラスターモード

センチネルモードは、読み取りと書き込みの分離を実現するマスター/スレーブモードをベースにしており、自動的に切り替えることもでき、システムの可用性が高くなります。ただし、各ノードに保存されるデータは同じであるため、メモリを無駄に消費し、オンラインで拡張するのは簡単ではありません。そこで、Redis3.0で追加され、 Redisの分散ストレージを実現したClusterクラスタが登場しました。データをシャーディングします。つまり、各 Redis ノードに異なるコンテンツを保存して、オンライン拡張の問題を解決します。また、レプリケーションおよびフェイルオーバー機能も提供します。

クラスタクラスタノードの通信

Redis クラスターは複数のノードで構成されますが、ノードはどのように相互に通信しますか? ゴシッププロトコルを通過してください!

Redis Cluster クラスターは Gossip プロトコルを介して通信し、ノード障害、新しいノードの参加、マスター/スレーブ ノードの変更情報、スロット情報などの情報をノード間で継続的に交換します。一般的に使用されるゴシップ メッセージは、ping、pong、meet、fail の 4 つのタイプに分類されます。

  • メッセージに会う: 新しいノードに参加するよう通知します。メッセージ送信者は受信者に現在のクラスタへの参加を通知し、meet メッセージ通信が正常に完了した後、受信ノードはクラスタに参加し、定期的に ping および pong メッセージを交換します。
  • Ping メッセージ: クラスター内で最も頻繁に交換されるメッセージ。クラスター内の各ノードは、ノードがオンラインかどうかを検出し、相互にステータス情報を交換するために、1 秒ごとに他の複数のノードに ping メッセージを送信します。
  • Pong メッセージ: ping または meets メッセージを受信すると、メッセージが正常に通信されたことを確認するための応答メッセージとして送信者に返信します。pong メッセージは、自身の状態データを内部的にカプセル化します。ノードは、独自の pong メッセージをクラスターにブロードキャストして、クラスター全体に自身のステータスを更新するように通知することもできます。
  • 失敗メッセージ: ノードがクラスター内の別のノードがオフラインであると判断すると、クラスターに失敗メッセージをブロードキャストし、他のノードは失敗メッセージを受信した後、対応するノードをオフライン状態に更新します。

特に、各ノードはクラスタ バスを介して他のノードと通信します。通信する場合は、外部サービスのポート番号に 10000 を加算した特別なポート番号を使用します。たとえば、ノードのポート番号が 6379 の場合、他のノードと通信するポート番号は 16379 になります。ノード間の通信には特別なバイナリ プロトコルが使用されます。

ハッシュ スロット スロット アルゴリズム

分散ストレージであるため、クラスターで使用される分散アルゴリズムは一貫性のあるハッシュですか? いいえ、ただしハッシュ スロット スロット アルゴリズムです。

スロット アルゴリズムはデータベース全体を 16384 個のスロット (スロット) に分割し、Redis に入力される各キーと値のペアはキーに従ってハッシュされ、16384 個のスロットの 1 つに割り当てられます。使用されるハッシュ マップも比較的単純で、CRC16 アルゴリズムを使用して 16 ビット値を計算し、その後 16384 を法とします。データベース内の各キーはこれら 16384 個のスロットの 1 つに属し、クラスター内の各ノードはこれらの 16384 個のスロットを処理できます。

クラスター内の各ノードは、ハッシュ スロットの一部を担当します。たとえば、現在のクラスターには A、B、および C ノードがあり、各ノードのハッシュ スロットの数 = 16384/3 の場合、次のようになります。

  • ノード A はハッシュ スロット 0 ~ 5460 を担当します。
  • ノード B はハッシュ スロット 5461 ~ 10922 を担当します。
  • ノード C はハッシュ スロット 10923 ~ 16383 を担当します

Redis クラスター クラスター

Redis Cluster クラスターでは、16384 個のスロットに対応するノードが正常に動作していることを確認する必要があり、ノードに障害が発生すると、そのノードが担当するスロットにも障害が発生し、クラスター全体が動作しなくなります。

したがって、高可用性を確保するために、クラスター クラスターではマスター/スレーブ レプリケーションが導入され、マスター ノードは 1 つ以上のスレーブ ノードに対応します。他のマスター ノードがマスター ノード A に ping を送信するときに、半数以上のマスター ノードと A の間の通信がタイムアウトになると、マスター ノード A はダウンしていると見なされます。マスター ノードがダウンしている場合は、スレーブ ノードが有効になります。

Redis の各ノードには 2 つのものがあり、1 つはスロット (スロット) で、その値の範囲は 0 ~ 16383 です。もう 1 つはクラスターで、クラスター管理プラグインとして理解できます。アクセスするキーが到着すると、Redis は CRC16 アルゴリズムに基づいて 16 ビット値を取得し、結果のモジュロ 16384 を取得します。Jiangzi の各キーは、0 ~ 16383 の番号が付いたハッシュ スロットに対応します。この値を通じて、対応するスロットに対応するノードを見つけ、アクセス操作のために対応するノードに直接かつ自動的にジャンプします。

データは異なるノードに個別に保存されますが、クライアントにとってはクラスター全体が 1 つの全体とみなされます。クライアントは任意のノードに接続します。これは、単一インスタンスを操作する Redis と同じように見えます。クライアントによって操作されたキーが正しいノードに割り当てられていない場合、Redis はステアリング命令を返し、最終的に正しいノードをポイントします。これはブラウザー ページの 302 リダイレクト ジャンプに似ています。

フェイルオーバー

Redis クラスターは高可用性を実現し、クラスター内のノードに障害が発生した場合、フェイルオーバーを使用してクラスターが外部にサービスを提供できるようにします。

Redis クラスターは、ピンポン メッセージを通じて障害検出を実装します。この環境には、主観的なオフラインと客観的なオフラインが含まれます。

主観的オフライン:他のノードが利用できないとノードが信じている状態、つまりオフライン状態ですが、この状態は最終的な故障判断ではなく、あくまで一ノードの意見を表しており、誤判断が発生する可能性があります。

主観的なオフライン

目的のオフライン:ノードが実際にオフラインであり、クラスター内の複数のノードがそのノードが利用できないと信じているため、合意結果に達しているという事実を指します。スロットを保持しているプラ​​イマリ ノードに障害が発生した場合、そのノードのフェイルオーバーが必要になります。

  • ノード A がノード B を主観的にオフラインとしてマークした場合、一定時間が経過すると、ノード A はメッセージを通じてノード B のステータスを他のノードに送信します。ノード C がメッセージを受信して​​メッセージ本文を解析すると、pfail ステータスが見つかった場合、ノード B は、目的のオフライン プロセスをトリガーします。
  • オフライン マスター ノードがオフラインの場合、Redis Cluster クラスターはスロットを保持するマスター ノードに投票し、投票数が半分に達するかどうかを確認します。オフライン レポートの統計が半分を超える場合、それは 客観的なオフライン状態 としてマークされます

プロセスは次のとおりです。

客観的にオフラインにする

障害回復: 障害が発見された後、オフライン ノードがマスター ノードの場合、クラスターの高可用性を確保するために、そのスレーブ ノードの 1 つを選択して置き換える必要があります。プロセスは次のとおりです。

  • 適格性チェック: スレーブ ノードが障害が発生したマスター ノードを置き換える条件を備えているかどうかを確認します。
  • 選出時間を準備する: 適格性チェックに合格した後、トリガー失敗の選出時間を更新します。
  • 選挙の開始: 失敗選挙の時間が来ると、選挙が実行されます。
  • 選挙投票:スロットを保持しているマスター ノードのみが投票を持ち、スレーブ ノードが十分な投票 (半分以上) を集めると、マスター ノードの置き換え操作がトリガーされます。

10. Redis 分散ロックを使用したことがありますか? 注意すべき点は何ですか?

分散ロックは、分散システム内のさまざまなプロセスを制御して共有リソースに一緒にアクセスするロックの実装です。分散ロックは、数秒で注文したり、赤い封筒を受け取ったりするなどのビジネス シナリオに必要です。Redis は、プロジェクトで分散ロックとしてよく使用されます。

Redis 分散ロックの実装方法をいくつか選択しました。問題があるかどうかを検討して確認してみましょう。

  • コマンド setnx +expire は別に記述されます
  • setnx + value value は有効期限です。
  • set用拡張コマンド(set ex px nx)
  • set ex px nx + 一意のランダム値を確認してから削除

10.1 コマンド setnx + 期限切れ書き込みを個別に実行する

if(jedis.setnx(key,lock_value) == 1){ //加锁 expire(key,100); //设置过期时间 try { do something //业务请求 }catch(){ } finally { jedis.del(key); //释放锁 } }

ロックが実行され、setnx有効期限を設定するために Expire が実行されようとしている場合、プロセスがクラッシュするか、メンテナンスのために再起動する必要がある場合、ロックは「不滅」になり、他のスレッドはロックを取得できなくなります。したがって、分散ロックはこのようには達成できません

10.2 setnx + 値は有効期限です

long expires = System.currentTimeMillis() + expireTime; //系统时间+设置的过期时间 String expiresStr = String.valueOf(expires); // 如果当前锁不存在,返回加锁成功 if (jedis.setnx(key, expiresStr) == 1) { return true; } // 如果锁已经存在,获取锁的过期时间 String currentValueStr = jedis.get(key); // 如果获取到的过期时间,小于系统当前时间,表示已经过期 if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) { // 锁已过期,获取上一个锁的过期时间,并设置现在锁的过期时间(不了解redis的getSet命令的小伙伴,可以去官网看下哈) String oldValueStr = jedis.getSet(key_resource_id, expiresStr); if (oldValueStr != null && oldValueStr.equals(currentValueStr)) { // 考虑多线程并发的情况,只有一个线程的设置值和当前值相同,它才可以加锁 return true; } } //其他情况,均返回加锁失败 return false; }

著者は、一部の開発者がこの方法で分散ロックを実装していることを見てきましたが、この解決策には次のような欠点もあります。

  • 有効期限はクライアント自体によって生成されるため、分散環境では各クライアントの時間を同期する必要があります。
  • 所有者の固有の識別子は保存されず、他のクライアントによって解放/ロック解除される可能性があります。
  • ロックの有効期限が切れると、複数の同時クライアントが同時に要求し、すべてが実行されます。jedis.getSet()最終的にロックに成功できるのは 1 つのクライアントだけですが、クライアントのロックの有効期限が他のクライアントによって上書きされる可能性があります。

10.3: 拡張コマンドの設定 (set ex px nx) (考えられる問題に注意してください)

if(jedis.set(key, lock_value, "NX", "EX", 100s) == 1){ //加锁 try { do something //业务处理 }catch(){ } finally { jedis.del(key); //释放锁 } }

この解決策には問題が発生する可能性があります。

  • ロックの有効期限が切れて解放されましたが、ビジネスはまだ実行されていません。
  • ロックは別のスレッドによって誤って削除されました。

10.4 set ex px nx + 一意のランダム値を確認してから削除

if(jedis.set(key, uni_request_id, "NX", "EX", 100s) == 1){ //加锁 try { do something //业务处理 }catch(){ } finally { //判断是不是当前线程加的锁,是才释放 if (uni_request_id.equals(jedis.get(key))) { jedis.del(key); //释放锁 } } }

ここでは、現在のスレッドによって追加および解放されたロックがアトミック操作であるかどうかが判断されます。jedis.del() を呼び出してロックを解放すると、ロックは現在のクライアントに属さなくなる可能性があり、他のユーザーによって追加されたロックは解放されます

一般に、代わりにlua スクリプトが使用されます。lua スクリプトは次のとおりです。

if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end;

この方法は非常に優れており、通常の状況では、この実装方法はすでに使用できます。ただし、ロックが期限切れになってロックが解除され、まだ業務が実行されていないという問題があります(実際、業務処理時間の見積もりには通常問題ありません)。

11.レディソンを使ったことがありますか? その原理について話す

分散ロックでは、ロックの有効期限が切れて解放され、業務が完了しないという問題が発生する可能性がありますロックの有効期限をもう少し長く設定すれば十分だと考える友人もいます。実際に、ロックを取得したスレッドに対してタイミング デーモン スレッドを起動して、時々ロックがまだ存在するかどうかを確認できるかどうかを考えてみましょう。ロックが早期に解除されるのを防ぎます。

現在のオープンソース フレームワーク Redisson は、この分散ロックの問題を解決します。Redisson の基礎となる原則を見てみましょう。

スレッドが正常にロックされている限り、ウォッチドッグが開始されますwatch dog。これは 10 秒ごとにチェックするバックグラウンド スレッドです。スレッド 1 がまだロックを保持している場合、ロック キーの有効期間は継続的に延長されます。したがって、Redisson は、ロックの有効期限と解放の問題を解決するために Redisson を使用し、業務の実行が完了しません

12. Redlock アルゴリズムとは何ですか

Redis は通常クラスターにデプロイされますが、データがマスターとスレーブの同期プロセスにあり、マスター ノードがハングアップすると仮定すると、 Redis 分散ロックでどのような問題が発生する可能性がありますか? このフローチャートを見てみましょう。

スレッド 1 が Redis のマスター ノードのロックを取得したが、ロックされたキーがスレーブ ノードに同期されていない場合。このとき、マスターノードに障害が発生した場合、スレーブノードがマスターノードにアップグレードされます。スレッド 2 は同じキーのロックを取得できますが、スレッド 1 はすでにロックを取得しているため、ロックのセキュリティは失われます。

この問題を解決するために、Redis の作者 antirez は高度な分散ロック アルゴリズムRedlockを提案しました。Redlock の核となるアイデアは次のとおりです。

複数の Redis マスター デプロイメントを実行して、それらが同時にダウンしないようにします。これらのマスター ノードは互いに完全に独立しており、それらの間でデータの同期は行われません。同時に、複数のマスター インスタンスのロックの取得と解放に、Redis の単一インスタンスの場合と同じメソッドが使用されていることを確認する必要があります。

現在 5 つの Redis マスター ノードがあり、これらの Redis インスタンスが 5 つのサーバーで実行されていると想定します。

RedLock の実装手順は次のとおりです。

  • 1. 現在時刻をミリ秒単位で取得します。
  • 2. 5 つのマスター ノードに順番にロックを要求します。クライアントはネットワーク接続と応答のタイムアウト期間を設定します。タイムアウト期間はロックの有効期限よりも短くする必要があります。(自動ロックの有効期限が 10 秒であると仮定すると、タイムアウト期間は通常 5 ~ 50 ミリ秒の間になります。タイムアウト期間が 50 ミリ秒であると仮定します)。タイムアウトになった場合は、マスター ノードをスキップして、できるだけ早く次のマスター ノードを試してください。
  • 3. クライアントは、現在の時刻からロックの取得の開始時刻 (つまり、手順 1 で記録された時刻) を減算して、ロックの取得に使用された時間を取得します。Redis マスター ノードの半分以上 (N/2+1、ここでは 5/2+1=3 ノード) がロックを取得し、使用時間がロックの有効期限よりも短い場合に限り、ロックは成功したと考えられます。(上に示すように、10 秒 > 30 ミリ秒 + 40 ミリ秒 + 50 ミリ秒 + 4 分 0 秒 + 50 ミリ秒)
  • ロックが取得されると、キーの実際の有効時間が変更されるため、ロックの取得に使用された時間を差し引く必要があります。
  • ロックの取得が失敗した場合(少なくとも N/2+1 のマスター インスタンスでロックが取得されなかった場合、またはロックの取得時間が有効時間を超えた場合)、クライアントはすべてのマスター ノードのロックを解除する必要があります(一部のマスター ノードにロックが解除されていない場合でも)。ロックが成功した場合は、一部の人がネットをすり抜けるのを防ぐためにロックを解除する必要もあります)。

簡略化された手順は次のとおりです。

  • 5つのマスターノードから順番にロックをリクエストします
  • 設定されたタイムアウト時間に応じてマスターノードをスキップするかどうかを判断します。
  • 3 ノード以上のロックに成功し、使用時間がロックの有効期間未満であれば、ロックが成功したと判断できます。
  • ロックの取得に失敗した場合は、ロックを解除してください。

13. Redis ジャンプテーブル

ジャンプ台

  • ジャンプ テーブルは、順序付きセット zset の基礎となる実装の 1 つです。
  • スキップ テーブルは、平均O(logN)および最悪 O(N) の複雑さのノード ルックアップをサポートし、順次操作を通じてノードをバッチ処理することもできます。
  • スキップ リストの実装は、zskiplist と zskiplistNode の 2 つの構造で構成されます。zskiplist はスキップ リスト情報 (ヘッダー ノード、テール ノード、長さなど) を保存するために使用され、zskiplistNode はスキップ リストを表すために使用されます。ノード
  • ジャンプ テーブルはリンク リストに基づいており、検索効率を向上させるためにマルチレベルのインデックスが追加されています。

14. MySQL と Redis が二重書き込みの一貫性を確保する方法

  • キャッシュ遅延二重削除
  • キャッシュの削除再試行メカニズム
  • biglog を非同期的に読み取り、キャッシュを削除します

14.1 二重削除の遅延?

遅延二重削除とは何ですか? フローチャートは次のとおりです。

遅延した二重削除プロセス

  1. まずキャッシュを削除してください
  2. データベースを再度更新します
  3. しばらく(たとえば 1 秒)スリープしてから、再度キャッシュを削除します。

これって一体どれくらい寝てるんですか?全部1秒?

このスリープ時間 = ビジネス ロジック データの読み取りに費やした時間 + 数百ミリ秒。読み取りリクエストの終了を確実にするために、書き込みリクエストは、読み取りリクエストによってもたらされる可能性のあるキャッシュのダーティ データを削除できます。

この種のソリューションは悪くはありません。スリープ期間中 (たとえば 1 秒) のみ、ダーティ データが存在する可能性があるため、一般的なビジネスでは受け入れられます。しかし、キャッシュの削除が 2 回目に失敗した場合はどうなるでしょうか? キャッシュとデータベースのデータはまだ不整合である可能性がありますよね? キーが自動的に期限切れになるように、キーの自然有効期限を設定してみてはいかがでしょうか。企業は有効期限内にデータの不整合を受け入れる必要がありますか? それとも別のより良い解決策があるのでしょうか?

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

二重削除が遅延しているため、2 番目のキャッシュ削除ステップの失敗によってデータの不整合が発生する可能性があります。このスキームを使用して最適化できます。削除が失敗した場合は、さらに数回削除し、キャッシュの削除が成功することを確認します。そのため、キャッシュの削除の再試行メカニズムを導入できます。

キャッシュを削除してプロセスを再試行してください

  1. データベースを更新するための書き込みリクエスト
  2. 何らかの理由でキャッシュの削除に失敗しました
  3. 削除に失敗したキーをメッセージキューに入れます
  4. メッセージキューのメッセージを消費し、削除するキーを取得します
  5. キャッシュ削除操作を再試行します

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

キャッシュ メカニズムの削除を再試行しても問題ありませんが、大量のビジネス コード侵入が発生します。実際、この方法で最適化することもできます。キーはデータベースのバイナリログを通じて非同期的に削除されます。

mysqlを例に挙げてみましょう

  • Ali の運河を使用して、binlog ログ収集を MQ キューに送信できます。
  • 次に、ACK メカニズムを通じてこの更新メッセージの処理を確認し、キャッシュを削除して、データ キャッシュの一貫性を確保します。

15. Redis が 6.0 以降マルチスレッドに変更されたのはなぜですか?

  • Redis 6.0 より前は、Redis がソケットの読み取り、解析、実行、書​​き込みなどのクライアント リクエストを処理する場合、シリアル メイン スレッド、いわゆる「シングル スレッド」によって処理されていました。
  • Redis はなぜ 6.0 より前にマルチスレッドを使用しなかったのですか? Redis を使用する場合、CPU がボトルネックになる状況はほとんどなく、主にメモリとネットワークによって制限されます。たとえば、通常の Linux システムでは、Redis はパイプライン処理を使用して 1 秒あたり 100 万のリクエストを処理できるため、アプリケーションが主に O(N) または O(log(N)) コマンドを使用する場合、CPU を過剰に消費することはほとんどありません。

Redis でのマルチスレッドの使用は、シングル スレッドを完全に放棄することを意味するものではありません。Redis は引き続きシングル スレッド モデルを使用してクライアント要求を処理しますが、データの読み取りと書き込み、プロトコル分析の処理、およびコマンドの実行にはマルチ スレッドを使用します。単一のスレッドを使用します。

この目的は、redis のパフォーマンスのボトルネックが CPU ではなくネットワーク IO にあるためで、マルチスレッドを使用すると IO の読み取りと書き込みの効率が向上し、それによって redis の全体的なパフォーマンスが向上します。

16. Redis トランザクション メカニズムについて話す

Redis は、MULTI、EXEC、WATCHなどの一連のコマンドを通じてトランザクション メカニズムを実装します。トランザクションは一度に複数のコマンドの実行をサポートしており、トランザクション内のすべてのコマンドはシリアル化されます。トランザクション実行プロセス中、キュー内のコマンドは順番にシリアル化され、他のクライアントによって送信されたコマンド要求はトランザクション実行コマンド シーケンスに挿入されません。

つまり、Redis トランザクションは、キュー内の一連のコマンドを順次、1 回だけ、排他的に実行します。

Redis は次のようにトランザクション プロセスを実行します。

  • トランザクションの開始 (MULTI)
  • コマンドエンキュー
  • トランザクションの実行 (EXEC)、トランザクションの取り消し (DISCARD)

17. Redisのハッシュ競合への対処方法

KV メモリ データベースとして、Redis はグローバル ハッシュを使用してすべてのキーと値のペアを保存します。このハッシュ テーブルは複数のハッシュ バケットで構成されており、ハッシュ バケットのエントリ要素には _key および _value ポインターが格納されます (*key は実際のキーを指し、*value は実際の値を指します)。

ハッシュ テーブルの検索速度は非常に速く、Java の HashMap に少し似ており、O(1) の時間計算量でキーと値のペアをすばやく見つけることができます。まずキーを通じてハッシュ値を計算し、対応するハッシュ バケットの場所を見つけてから、エントリを見つけて、エントリ内の対応するデータを見つけます。

ハッシュ衝突とは何ですか?

ハッシュの競合: 異なるキーを使用して同じハッシュ値が計算され、結果として同じハッシュ バケットが生成されます。

ハッシュの競合を解決するために、Redis は連鎖ハッシュを使用します。連鎖ハッシュとは、同じハッシュ バケット内に複数の要素がリンク リストに格納され、それらがポインタで順番に接続されることを意味します。

一部の読者はまだ疑問を抱いているかもしれません。ハッシュ衝突チェーン上の要素は、ポインタを介して 1 つずつ検索および操作するしかありません。大量のデータがハッシュ テーブルに挿入されると、競合が多くなり、競合リストが長くなり、クエリの効率が低下します。

高い効率を維持するために、Redis はハッシュ テーブルに対して再ハッシュ操作を実行します。これは、ハッシュ バケットを追加して競合を減らすことを意味します。再ハッシュをより効率的にするために、Redis はデフォルトで 2 つのグローバル ハッシュ テーブルも使用します。1 つはメイン ハッシュ テーブルと呼ばれる現在使用用で、もう 1 つは拡張用でありスタンバイ ハッシュ テーブルと呼ばれます

18. RDB の生成中に、Redis は書き込みリクエストを同時に処理できますか?

はい、Redis には、RDB を生成するための 2 つのコマンド、save と bgsaveが用意されています。

  • 保存命令の場合、メインスレッドで実行されるためブロックされます。
  • bgsave コマンドの場合、RDB ファイルを書き込むために子プロセスがフォークされ、スナップショットの永続化は子プロセスによって完全に処理され、親プロセスはクライアントのリクエストを処理し続けることができます。

19. Redis は最下層でどのようなプロトコルを使用しますか?

RESP、正式な英語名は Redis Serialization Protocol で、redis 専用に設計されたシリアル化プロトコルで、実際には redis バージョン 1.2 から登場しましたが、redis2.0 で最終的に redis 通信プロトコルの標準となりました。

RESP には主に、実装が簡単、解析速度が速く、読みやすいという利点があります。

20. ブルームフィルター

キャッシュの侵入の問題に対処するには、ブルーム フィルターを使用できますブルームフィルターとは何ですか?

ブルーム フィルターは、フットプリントが小さいデータ構造です。非常に長いバイナリ ベクトルと一連のハッシュ マッピング関数で構成されます。要素がセット内にあるかどうかを取得するために使用されます。スペース効率とクエリ時間は、ブルーム フィルターよりも高速です。一般的なアルゴリズムの方がはるかに優れていますが、一定の割合で誤認識が発生し、削除が困難であるという欠点があります。

ブルームフィルターの原理は何ですか?A に n 個の要素を含むコレクション A があるとします。k 個のハッシュ関数を使用して、A の各要素が長さ a ビットの配列 B の異なる位置にマッピングされ、これらの位置の 2 進数はすべて 1 に設定されます。チェックされる要素が k 個のハッシュ関数によってマップされ、その k 個の位置の 2 進数がすべて 1 であることが判明した場合、この要素はおそらく集合 A に属します。そうでない場合は、集合 A に属してはなりません。

集合 A に 3 つの要素、つまり { d1, d2, d3 } があると仮定した簡単な例を見てみましょう。1 つのハッシュ関数Hash1があります次に、A の各要素を長さ 16 ビットの配列 B にマップします。

次に、Hash1(d1) = 2 と仮定して d1 をマッピングし、次のように配列 B の添え字 2 を持つグリッドを 1 に変更します。

次に、Hash1(d2) = 5 と仮定してd2をマッピングし、次のように配列 B の添字 5 グリッドも 1 に変更します。

次に、Hash1 (d3) も 2 に等しいと仮定して、d3 もマッピングします。これにより、添え字 2 のグリッドも 1 としてマークされます

したがって、要素 dn が集合 A にあるかどうかを確認したい場合は、Hash1(dn) で得られるインデックス添字を計算するだけで済みます。それが 0 である限り、この要素は集合 A に含まれていないことを意味します。インデックスの添字が1の場合はどうなるでしょうか? その場合、その要素はA の要素である可能性があります。ご覧のとおり、d1 と d3 によって取得される添え字の値は両方とも 1 である可能性があり、他の数値によってマップされる場合もあります。ブルーム フィルターには次の欠点があります。ハッシュの衝突によって誤検出が発生し、エラーが発生します判断致します

このエラーを減らすにはどうすればよいでしょうか?

  • ハッシュ衝突の可能性を減らすために、より多くのハッシュ関数マッピングを実行します。
  • 同時に、B 配列のビット長を増やすと、ハッシュ関数によって生成されるデータの範囲が広がり、ハッシュ衝突の確率が減少します。

次のように、別の Hash2ハッシュ マッピング関数を追加します。Hash2 (d1) = 6、Hash2 (d3) = 8 と仮定すると、これらは競合しません。

たとえエラーがあったとしても、ブルーム フィルターは完全なデータを保存しておらず、一連のハッシュ マッピング関数を使用して位置を計算し、バイナリ ベクトルを埋めているだけであることがわかります。数値が大きい場合、ブルーム フィルターのエラー率は非常に低くなり、その代わりにストレージ スペースが大幅に節約され、非常にコスト効率が高くなります。

現時点では、Bloom フィルターには、Google の Guava ライブラリや Twitter の Algebird ライブラリなど、対応するオープン ソース ライブラリがすでに存在しており、簡単に入手できます。また、Redis に付属するビットマップに基づいて、自分で設計を実装することも可能です。

おすすめ

転載: blog.csdn.net/github_36665118/article/details/128845139