この記事では、Redis の面接でよくある質問をいくつか整理して共有し、データ構造、メモリ モデル、IO モデル、永続 RDB などを含む Redis の中核となる知識ポイントについて説明します。あなたのお役に立ちますように!
多くの人は、これが K/V NoSQl インメモリ データベース、シングル スレッドであることしか知りません。これは、Redis を完全には理解しておらず、これ以上質問を続けることができないためです。
この質問は基本的な理解です。完全にメモリ、IO 多重化ネットワーク モデル、スレッド モデル、プログレッシブ リハッシュに基づいて、Redis のさまざまなデータ型の基礎となるデータ構造から実装できます...
###どのくらい速いのか? まず、その速さについて話します。公式データによると、Redis の QPS は約 100,000 (1 秒あたりのリクエスト数) に達します。興味がある方は、公式ベンチマーク プログラム「Redis はどのくらい速いですか?」を参照してください。 」 》、アドレス: 横軸は接続数、縦軸はQPSです。この図は桁違いに反映されており、数値化することで面接官に公式文書を読んで非常に厳格であると感じさせます。メモリベースの実装Redis はメモリベースのデータベースであり、ディスク データベースと比較すると、ディスクの速度を完全に上回ります。 読み取り操作と書き込み操作は両方ともメモリ内で完了します。メモリ操作とディスク操作の違いをそれぞれ比較してみましょう。
ディスク呼び出し
メモリ操作
メモリは CPU によって直接制御されますまた、CPU 内に統合されたメモリ コントローラーであるため、メモリは CPU に直接接続され、CPU との通信に最適な帯域幅を享受します。 最後に、画像を使用してシステムのさまざまな遅延時間を定量化します (データの一部は Brendan Gregg から引用) 効率的なデータ構造MySQL を学習していたとき、検索速度を向上させるために B Tree データ構造が使用されていることを知っていたので、Redis の高速性もデータ構造に関係しているはずです。 Redis には、String、List、Hash、Set、SortedSet の合計 5 つのデータ型があります。
マー兄弟のメッセージ: 各データ型の基礎となるデータ構造の利点を個別に説明できます。多くの人はデータ型しか知りませんが、基礎となるデータ構造を説明することで人々の目を輝かせることができます。
#SDS の単純な動的文字列の利点
リストに含まれるデータが少量で、各リスト項目が小さな整数値または比較的短い文字列の場合、Redis はリスト キーの基になる実装として圧縮リストを使用します。
#これにより、メモリがコンパクトになり、メモリが節約されます。
クイックリスト後続のバージョンでは、ジップリストとリンクリストの代わりにクイックリストを使用して、リストのデータ構造が変更されました。クイックリストは、ジップリストとリンクリストを組み合わせたものです。リンクリストをセグメントに分割します。各セグメントはコンパクトなストレージのためにジップリストを使用し、複数のジップリストは双方向ポインタを使用して直列に接続されます。
skipList スキップ リスト
ソート セット タイプのソート機能は、「スキップ リスト」データ構造を通じて実装されます。スキップ リスト (スキップリスト) は、ノードに迅速にアクセスするという目的を達成するために、各ノード内の他のノードへの複数のポインターを維持する順序付けされたデータ構造です。
次の図に示すように、スキップ リストはリンク リストに基づいてマルチレベルのインデックスを追加し、インデックス位置の複数のジャンプを通じてデータの迅速な配置を実現します。
##整数配列 (intset)
セットに整数値要素のみが含まれており、このセット内の要素の数が多くない場合、Redis は基になる実装として整数セットを使用します。設定されたキーのメモリを節約します。 シングルスレッド モデルRedis の永続化、クラスターデータの同期、非同期削除などは、すべて他のスレッドによって実行されます。Redis にはスレッドが 1 つしかないなどとは言わないでください。
シングル スレッドとは、Redis のキーと値のペアの読み取りおよび書き込み命令を 1 つのスレッドで実行することを指します。
最初に、他の人の意見に基づいていくつかのブログを引用するのではなく、人々に十分な厳しさを感じさせる正式な答えについて話しましょう。
正式な回答:
Redis はメモリベースの操作であるため、CPU が Redis のボトルネックではありません。Redis のボトルネックは、マシンのメモリまたはネットワーク帯域幅。シングルスレッドは実装が簡単で、CPU がボトルネックにならないため、シングルスレッド ソリューションを採用するのは合理的です。元のアドレス: redis.io/topics/faq。 マルチスレッド実行を使用して CPU を最大限に活用してみてはいかがでしょうか?
各タスクを実行する前に、CPU はタスクがどこでロードされ、実行が開始されたかを認識する必要があります。つまり、システムは、CPU コンテキストと呼ばれる、事前に CPU レジスタとプログラム カウンタを設定するための支援を必要とします。コンテキストを切り替えるときは、一連の作業を完了する必要がありますが、これは非常にリソースを消費する操作です。
マルチスレッド開発を導入するには、共有リソースの同時読み取りと書き込みを保護するために同期プリミティブを使用する必要があり、コードが複雑になり、デバッグが困難になります。
シングルスレッドの利点は何ですか?コンテキストの切り替えによる CPU の消費を回避し、マルチスレッド切り替えのオーバーヘッドはありません。スレッドの作成によるパフォーマンスの消費はありません。
Redis グローバル ハッシュ ディクショナリ
Redis 全体は、データ型が 5 つのタイプのいずれであるかに関係なく、すべてのキーと値のペアを保存するハッシュ テーブルです。ハッシュ テーブルは本質的に配列であり、各要素はハッシュ バケットと呼ばれ、データ型に関係なく、各バケット内のエントリは実際の特定の値へのポインタを保持します。
ハッシュ テーブルの時間計算量は O(1) です。各キーのハッシュ値を計算するだけで、対応するハッシュ バケットの位置と位置を知ることができます。バケット内のエントリが対応するデータを見つけることも、Redis が高速である理由の 1 つです。Redis はオブジェクト (redisObject) を使用してデータベース内のキー値を表します。Redis でキーと値のペアを作成すると、少なくとも 2 つのオブジェクトが作成されます。1 つのオブジェクトは、キーと値のペア。もう 1 つはキーと値のペアの値オブジェクトです。
つまり、各エントリには「キーと値のペア」の redisObject オブジェクトが格納され、対応するデータは redisObject のポインターを通じて検索されます。
typedef struct redisObject{ //类型 unsigned type:4; //编码 unsigned encoding:4; //指向底层数据结构的指针 void *ptr; //... }robj;
ハッシュ競合どうすればいいですか?
Redis は
チェーン ハッシュ。ただし、リンクリストが長すぎると検索性能が低下する可能性があるため、Redisでは高速性を追求するために2つのグローバルハッシュテーブルを使用しています。既存のハッシュ バケットの数を増やし、ハッシュの競合を減らすための再ハッシュ操作に使用されます。 デフォルトでは、キーと値のペアのデータを保存するために「ハッシュ テーブル 1」を使用して開始します。現時点では、「ハッシュ テーブル 2」にはスペースが割り当てられていません。ますます多くのデータが再ハッシュ操作をトリガーすると、次の操作が実行されます。
データをハッシュ テーブル 1 からハッシュ テーブル 2 に再マッピングするプロセスは 1 回限りのプロセスではないことに注意してください。これにより、Redis がブロックされ、サービスを提供できなくなります。 。
代わりに、プログレッシブ リハッシュが使用されます。クライアント リクエストが処理されるたびに、「ハッシュ テーブル 1」の最初のインデックスから開始され、この位置が再ハッシュされます。すべてのデータは「ハッシュ テーブル 2」にコピーされ、時間のかかるブロックを避けるために再ハッシュが複数のリクエストに分散されます。
Redis のデータ永続化では「RDB データ スナップショット」方式を採用し、ダウンタイムからの迅速な復旧を実現します。ただし、完全なデータ スナップショットを頻繁に実行すると、次の 2 つの重大なパフォーマンス オーバーヘッドが発生します。
そのため、Redis は、メモリを変更するための指示を記録するための AOF 書き込み後ログも設計しました。
インタビュアー: RDB メモリ スナップショットとは何ですか?
Redis が「write」コマンドを実行すると、メモリ データは変更され続けます。いわゆるメモリ スナップショットとは、ある時点での Redis メモリ内のデータのステータス データを指します。
それは、ある瞬間に時間が止まっているようなもので、写真を撮ると、その瞬間を写真として完全に記録することができます。
Redis もこれに似ており、特定の瞬間のデータをファイルの形式でキャプチャし、ディスクに書き込みます。このスナップショットファイルは RDB ファイルと呼ばれます (RDB は Redis DataBase の略です)。
#データリカバリを行う場合、RDBファイルをメモリ上に直接読み込んでリカバリを完了します。
インタビュアー: RDB の生成中に、Redis は書き込みリクエストを同時に処理できますか?
はい、Redis はオペレーティング システムのマルチプロセス コピー オン ライト テクノロジ COW (コピー オン ライト) を使用して、スナップショットの永続性を実現し、データの一貫性を確保します。
Redis は永続化 fork
中に glibc 関数を呼び出して子プロセスを生成します。スナップショットの永続化は子プロセスによって完全に処理され、親プロセスはクライアント要求の処理を続行します。
メインスレッドが書き込みコマンドを実行してデータを変更すると、データがコピーされます bgsave
子プロセスはコピーデータを読み取り、RDB ファイルに書き込みます。
これにより、スナップショットの整合性が保証されるだけでなく、メインスレッドが同時にデータを変更できるようになり、通常のビジネスへの影響が回避されます。
インタビュアー: それで、AOF とは何ですか?
AOF ログには、Redis インスタンスの作成以降に変更されたすべての命令シーケンスが記録されます。その後、空の Redis インスタンスですべての命令を順番に実行する、つまり「再生」することで復元できます。 Redis の現在のインスタンスのメモリ データ構造の状態。
Redis によって提供される AOF 構成アイテムappendfsync
ライトバック戦略は、AOF 永続化機能の効率とセキュリティを直接決定します。
aof_buf
バッファー内の内容が AOF ファイルに書き込まれます。 両方の利点を生かした戦略は存在しません。パフォーマンスと信頼性の間でトレードオフを行う必要があります。
インタビュアー: RDB には 2 つのパフォーマンスの問題があるため、AOF を使用しないのはなぜでしょうか。
AOF 書き込み前ログは、各「書き込み」コマンド操作を記録します。 RDB フルスナップショットのようなパフォーマンスの低下はありませんが、実行速度は RDB ほど速くなく、同時にログ ファイルが大きすぎるとパフォーマンスの問題も発生します。
そこで、Redis はキラーな「AOF 書き換えメカニズム」を設計しました。Redis は、AOF ログをスリム化するための bgrewriteaof
命令を提供します。
原理は、サブプロセスを開いてメモリを走査し、それを一連の Redis 操作命令に変換し、新しい AOF ログ ファイルにシリアル化することです。シリアル化完了後、運用中に発生した増分AOFログが新しいAOFログファイルに追記され、追記完了後直ちに古いAOFログファイルと置き換えられ、スリム化作業が完了します。
#インタビュアー: パフォーマンスを考慮しながら、データ損失を最小限に抑えるにはどうすればよいですか?
Redis を再起動する場合、大量のデータが失われるため、rdb を使用してメモリ状態を復元することはほとんどありません。通常はAOFログリプレイを使用しますが、AOFログリプレイはRDBに比べてパフォーマンスが非常に遅いため、Redisインスタンスが大きい場合には起動に時間がかかります。
この問題を解決するために、Redis 4.0 では新しい永続化オプション ハイブリッド永続化 が導入されました。 rdb ファイルの内容を増分 AOF ログ ファイルと一緒に保存します。ここでの AOF ログは完全なログではなく、永続化の開始から永続化の終了までの期間中に発生した増分 AOF ログ です。通常、AOF ログのこの部分は非常に小さいです。
SoRedis を再起動すると、最初に rdb コンテンツをロードしてから、増分 AOF ログを再生することができます。これにより、以前の AOF フル ファイル再生を完全に置き換えることができ、再起動の効率が大幅に向上します# # #。 Redis マスター/スレーブ アーキテクチャのデータ同期
レプリカ データの一貫性を確保するために、マスター/スレーブ アーキテクチャでは読み取り/書き込み分離方式が採用されています。読み取り操作: マスター ライブラリとスレーブ ライブラリの両方が実行可能;
障害回復: マスター ノードがダウンしても、他のノードは引き続きサービスを提供できます。
同期は 3 つの状況に分けられます:マスター/スレーブ ライブラリの最初の完全なコピー;
マスター/スレーブ データベースの最初のレプリケーション プロセスは、接続確立フェーズ (準備フェーズ)、マスターからのデータの同期フェーズの 3 つのフェーズに大別できます。データベースからスレーブ データベースへの移行、および同期中に新しいデータを送信するフェーズ。スレーブ ライブラリ ステージへのコマンドの書き込み
#接続を確立します:
は各スレーブに対して開きます。 レプリケーション バッファには、RDB ファイルの生成以降に受信したすべての書き込みコマンドが記録されます。ライブラリから RDB を保存し、データベースをクリアしてから、RDB データをメモリにロードします。
RDB 後に受信した新しい書き込みコマンドをスレーブ ライブラリに送信します: RDB ファイル生成後の書き込み操作は、先ほどの RDB ファイルには記録されません。マスター/スレーブ ライブラリ データの一貫性を確保するためです。 、マスター ライブラリ RDB ファイルの生成後に、すべての書き込み操作を記録するためにメモリ内でレプリケーション バッファが使用されます。そして内部のデータをスレーブに送信します。
Redis 2.8 以降、ネットワークが切断された後、マスター/スレーブ ライブラリは増分レプリケーションを使用して同期を継続します。 増分レプリケーション:Redis 2.8 より前は、コマンドの伝播中にマスター/スレーブ ライブラリでネットワーク中断が発生した場合、スレーブ ライブラリはマスター ライブラリのフル コピーを再度実行していましたが、これは非常にコストがかかりました。
ネットワーク中断などの後のレプリケーションに使用されます。中断中にマスター ノードによって実行された書き込みコマンドのみがスレーブ ノードに送信され、フル レプリケーションよりも効率的です## #。
増分レプリケーションの切断と再接続の秘密は、repl_backlog_buffer バッファです。メモリが限られているため、マスターはいつでも書き込み命令操作を
repl_backlog_buffer に記録します。 , repl_backlog_buffer
は固定長の循環配列であり、配列の内容がいっぱいの場合は、前の内容
を最初から上書きします。 マスターは master_repl_offset を使用して自身が書き込んだ位置オフセットを記録し、スレーブは
を使用して読み取られたオフセットを記録します。
runID## を変更します。 #, slave_repl_offset
マスターに送信します。マスターは、
master_repl_offset と
slave_repl_offset
増分コピーの実行プロセスは次のとおりです:
インタビュアー: 完全同期が完了した後、通常の操作中にデータを同期するにはどうすればよいですか?
マスター/スレーブ ライブラリが完全なレプリケーションを完了すると、それらの間のネットワーク接続が維持されます。マスター ライブラリはこの接続を使用して、連続して受信した後続のコマンド操作をスレーブ ライブラリに同期します。このプロセスもこれは長い接続に基づくコマンド伝播として知られていますが、長い接続を使用する目的は、頻繁な接続確立によって生じるオーバーヘッドを回避することです。
インタビュアー: もちろん、よく知っていますが、センチネル クラスター原則をご存知ですか?
Sentinel は Redis の動作モードです。Redis インスタンス (マスター ノード、スレーブ ノード) の実行ステータスの監視に焦点を当てており、マスター ノードに障害が発生した場合に一連のアクションを渡すことができます。この仕組みにより、マスター選択とマスター/スレーブ切り替えが実現され、フェイルオーバーが実現され、Redis システム全体の可用性が確保されます。
彼のアーキテクチャ図は次のとおりです: #Redis Sentinel には次の機能があります:インタビュアー: センチネルたちはどうやってお互いを知っているのですか?センチネルはマスターとの通信を確立し、マスターによって提供されるパブリッシュ/サブスクライブ メカニズムを使用して、身長と体重、独身かどうか、IP、ポートなどの独自の情報を公開します...マスターには #__sentinel__:hello
の専用チャネルがあり、センチネル間でメッセージをパブリッシュおよびサブスクライブするために使用されます。 これは
__sentinel__:hello WeChat グループのようなものです。センチネルはマスターによって設立された WeChat グループを使用して自分のメッセージを公開し、同時に他のセンチネルがリリースしたメッセージに注意を払います。
重要なのは、マスターを使用してそれを達成することです。センチネルはINFO
コマンドをマスターに送信します。マスターは当然、自分のセクトの下にあるすべてのスレーブを知っています。したがって、マスターがコマンドを受信すると、センチネルにスレーブのリストを伝えます。 センチネルは、マスターから応答されたスレーブリスト情報に基づいて各スレーブとの接続を確立し、この接続に基づいてセンチネルを継続的に監視します。
クラスター クラスター シリアル キャノン
インタビュアー: セントリー以外に、高可用性を実現する方法はありますか?高可用性を実現するためのクラスター クラスターがあります。Sentinel クラスターによって監視される Redis クラスターはマスター/スレーブ アーキテクチャを採用しているため、簡単に拡張できません。Redis Cluster クラスターを使用すると、主に大規模なデータ ストレージによって引き起こされるさまざまな速度低下の問題が解決され、水平方向の拡張も容易になります。
数百万または数千万のユーザーに直面する場合、水平方向にスケーラブルな Redis スライシング クラスターは非常に良い選択となります。
インタビュアー: クラスターとは何ですか?
Redis クラスターは分散データベース ソリューションであり、クラスターはシャーディング (「分割統治思考」の実践) を通じてデータを管理し、レプリケーションおよびフェイルオーバー機能を提供します。
データを 16384 個のスロットに分割し、各ノードがスロットの一部を担当します。スロット情報は各ノードに格納されます。
分散型です。図に示すように、クラスターは 3 つの Redis ノードで構成されます。各ノードはクラスター全体のデータの一部を担当します。各ノードが担当するデータの量は、異なる。
3 つのノードは相互に接続されてピアツーピア クラスターを形成し、
Gossip プロトコルを通じて相互にクラスター情報を交換します。そして最後に、各ノードは他のノードへのスロットの割り当てに応じて保存します。
キーと値のペアのキーに従って、CRC16 アルゴリズムを使用して 16 ビット値を計算します。
Redis クラスター ノードは、Gossip
プロトコルを使用して、自身のステータスとクラスター全体の知識の変更をブロードキャストします。たとえば、ノードが特定のノードの切断 (PFail) を検出すると、この情報がクラスター全体にブロードキャストされ、他のノードもこの切断された接続情報を受信できます。
ノードからの切断数 (PFail Count) がクラスターの大部分に達したことをノードが受信した場合、そのノードはオフライン (Fail) であると判断されたものとしてマークし、ノード全体にブロードキャストできます。他のノードもノードがオフラインになったことを強制的に受け入れ、失われたノードでマスター/スレーブの切り替えを直ちに実行します。
インタビュアー: クライアントは、アクセスされたデータがどのインスタンスに分散されているかをどのように判断しますか?
Redis インスタンスは、Gossip プロトコルを通じてクラスター内の他のインスタンスにハッシュ スロット情報を送信し、ハッシュ スロット割り当て情報の拡散を実現します。
このようにして、クラスター内の各インスタンスは、すべてのハッシュ スロットとインスタンスの間のマッピング関係情報を持ちます。
クライアントが任意のインスタンスに接続すると、インスタンスはハッシュ スロットとインスタンスの間のマッピング関係をクライアントに応答し、クライアントはハッシュ スロットとインスタンスのマッピング情報をローカルにキャッシュします。
クライアントがリクエストを行うと、キーに対応するハッシュ スロットが計算され、ローカルにキャッシュされたハッシュ スロット インスタンスのマッピング情報を通じてデータが配置されているインスタンスが特定され、リクエストは対応するインスタンスに送信されます。
#インタビュアー: Redis リダイレクト メカニズムとは何ですか?
ハッシュ スロットとインスタンスの間のマッピング関係は、新しいインスタンスまたは負荷分散の再分散により変更されました。クライアントはインスタンスにリクエストを送信しますが、このインスタンスには対応するデータがありません。 Redis インスタンスは、クライアントにリクエストを他のインスタンスに送信するよう指示します。
Redis は、MOVED エラーと ASK エラーを通じてクライアントに通知します。
MOVED エラー (負荷分散、データは他のインスタンスに移行されました): クライアントがキーと値のペアの操作リクエストをインスタンスに送信するとき、キーが配置されているスロットがそれ自体によって所有されていない場合、インスタンスは MOVED エラーを返し、スロットを担当するノードにリダイレクトされます。
同時に、クライアントはローカル キャッシュも更新し、スロットと Redis インスタンス の間の対応関係を正しく更新します。
ASK特定のスロットに大量のデータがある場合、その一部は新しいインスタンスに移行され、その一部は移行されません。 要求されたキーが現在のノードで見つかった場合は、コマンドを直接実行します。そうでない場合は、ASK エラー応答が必要になります。 スロットの移行が完了していない場合、アクセスする必要があるキーが存在するスロットがインスタンス 1 からインスタンス 2 に移行されている場合 (キーがインスタンス 1 に存在しない場合)、インスタンス 1 が返されます。 ASK エラー メッセージをクライアントに送信します:クライアントが要求したキーが存在するハッシュ スロットはインスタンス 2 に移行されます。まず ASKING コマンドをインスタンス 2 に送信し、次に操作コマンド を送信します。
たとえば、クライアントは、インスタンス 172.17.18.1 上のキー = "公式アカウント: コード バイト" を持つスロット 16330 を見つけるように要求します。ノード 1 がそれを見つけることができれば、コマンドを直接実行します。それ以外の場合は、応答します。 ASK エラー メッセージを表示し、クライアントを移行中のターゲット ノード 172.17.18.2 に誘導します。 注:ASK エラー コマンドは、クライアントのキャッシュされたハッシュ スロット割り当て情報 を更新しません。
概要この記事では主に、データ構造、メモリ モデル、IO モデル、永続 RDB と AOF、マスター/スレーブ レプリケーションの原理、センチネルの原理、クラスターの原理など、Redis の中核となる内容について説明します。 。元のアドレス: https://juejin.cn/post/6976257378094481444著者: Code Brother Byteその他のプログラミング関連の知識については、こちらをご覧ください。 :
プログラミングビデオ! !
以上が高頻度の Redis 面接の質問を共有すると、核となる知識ポイントを習得するのに役立ちます。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。