ホームページ > データベース > Redis > Redis の典型的な面接の 20 の質問と回答の要約 (共有)

Redis の典型的な面接の 20 の質問と回答の要約 (共有)

青灯夜游
リリース: 2023-03-07 18:53:41
転載
4678 人が閲覧しました

この記事では、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 つの基本タイプがあることを知っています:

String (Character String)
  • Hash (ハッシュ)
  • List (リスト)
  • Set (セット)
  • zset (順序セット)
  • It 3 つの特殊なデータ構造タイプもあります。

Geospatial
  • Hyperloglog
  • Bitmap
2.1 5 つの基本データ型Redis

String (string)

はじめに: String は Redis の最も基本的なデータ構造タイプであり、バイナリ セーフであり、画像またはシリアル化されたオブジェクトを保存できます。保存される最大値は 512M
  • 簡単な使用例:
  • set key value
  • get key など。アプリケーション シナリオ: 共有セッション、分散ロック、カウンター、電流制限。
  • 内部エンコードには
  • int (8 バイト長の整数)/embstr (39 バイト以下の文字列)/raw (39 バイトを超える文字列)の 3 種類があります
  • ##C 言語の文字列は
  • char[]
によって実装されますが、Redis は

SDS (単純な動的文字列) カプセル化を使用します。sds のソース コードは次のとおりです。 :

struct sdshdr{
  unsigned int len; // 标记buf的长度
  unsigned int free; //标记buf中未使用的元素个数
  char buf[]; // 存放元素的坑
}
ログイン後にコピー
SDS 構造図は次のとおりです:

C 言語ネイティブであるのに、Redis が SDS

構造を選択する理由

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

Hash (ハッシュ)

はじめに: Redis では、ハッシュ タイプは、キーと値のペア (k-v) である v (値) 自体を指します。構造

    簡単な使用例:
  • hset キー フィールド値
  • hget キー フィールド#内部エンコーディング: ziplist (圧縮リスト),
  • hashtable (ハッシュ テーブル)
  • アプリケーション シナリオ: ユーザー情報のキャッシュなど。
  • Note
  • : hgetall を開発に使用し、ハッシュ要素が多い場合、Redis がブロックされる可能性があります。hscan を使用できます。一部のフィールドのみを取得したい場合は、hmget を使用することをお勧めします。
  • 文字列型とハッシュ型の比較は次のとおりです:

List (リスト)

はじめに:リスト (リスト) タイプは、複数の順序付けされた文字列を格納するために使用され、リストには最大 2^32-1 個の要素を格納できます。

シンプルで実践的な例:
    lpush key value [value ...]
  • ,
  • lrange key start end
  • 内部エンコーディング: ziplist (圧縮リスト) )、linkedlist (リンク リスト)アプリケーション シナリオ: メッセージ キュー、記事リスト、
  • リスト タイプの挿入とポップアップを理解するための 1 つの図:

list アプリケーション シナリオは次を参照します。

lpush lpop=Stack (スタック)

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

はじめに: set 型は複数の文字列要素を保存するためにも使用されますが、要素の重複は許可されません。

簡単な使用例:
    sadd キーelement [element .. .]
  • ,
  • smembers key
  • 内部エンコーディング: intset (整数セット) ,
  • hashtable (ハッシュ テーブル)
  • 注意事項 : smembers、lrange、hgetall は比較的重いコマンドですが、要素が多すぎて Redis がブロックされる可能性がある場合は、sscan を使用して完了できます。
  • アプリケーションシナリオ: ユーザータグ、乱数宝くじの生成、ソーシャルニーズ。
  • オーダードセット (zset)
    • 概要: ソートされた文字列コレクション、要素は繰り返しできません
    • 簡単な形式の例:zadd キー スコア メンバー [スコア メンバー...], zrank キー メンバー
    • 基になる内部エンコーディング: ziplist (圧縮リスト)skiplist (スキップ リスト)
    • アプリケーション シナリオ: ランキング リスト、ソーシャル ニーズ(ユーザーの好みなど)。

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

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

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

    Redis が速い理由

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

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

    3.2 効率的なデータ構造

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

    #SDS 単純な動的文字列

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

    Dictionary

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

    ジャンプ テーブル

    • スキップ テーブルは Redis の独自のデータ構造であり、リンク リストに基づいて複数のデータを追加します。 -レベルインデックスの改善、検索効率。
    • スキップ テーブルは、平均 O(logN) および最悪の場合の O(N) 複雑さによるノード検索をサポートし、順次操作を通じてノードをバッチで処理することもできます。

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

    Redis は複数のデータ型をサポートしており、各基本型には複数のデータ構造がある場合があります。いつ、どのようなデータ構造を使用するか、どのエンコーディングを使用するかは、Redis デザイナーの要約と最適化の結果です。

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

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

    I/O 多重化

    I/ O 多重化

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

    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 キャッシュ侵入の問題

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

    キャッシュの読み取り

    キャッシュ侵入: 存在してはいけないデータのクエリを指します。これは、キャッシュがミスするために必要です。データベースからクエリを実行する場合、データが見つからない場合、そのデータはキャッシュに書き込まれません。これにより、存在しないデータがリクエストされるたびにデータベース内でクエリが行われることになり、データベースに負荷がかかります。

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

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

    • 不合理なビジネス設計。たとえば、ほとんどのユーザーは Guard を有効にしていませんが、すべてのユーザーが Guard を有効にしていません。作成したリクエストはキャッシュに保存され、特定のユーザー ID が保護されているかどうかがチェックされます。
    • ビジネス/運用/メンテナンス/開発エラー (キャッシュやデータベース データが誤って削除されるなど)。
    • ハッカーによる違法なリクエスト攻撃 たとえば、ハッカーは、存在しないビジネス データを読み取るための大量の違法なリクエストを意図的にでっち上げます。

    キャッシュの侵入を回避するにはどうすればよいですか? 一般に 3 つの方法があります。

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

    ブルーム フィルターの原理: 初期値 0 のビットマップ配列と N 個のハッシュ関数で構成されます。キーに対して N 個のハッシュ アルゴリズムを実行して N 個の値を取得します。これらの N 個の値をビット配列でハッシュし、1 に設定します。次に、チェック時に、これらの特定の位置がすべて 1 であれば、ブルーム フィルター処理が行われます。サーバーはキーが存在すると判断します。 。

    4.2 キャッシュ スノー ランの問題

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

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

    4.3 キャッシュ ブレークダウンの問題

    キャッシュ ブレークダウン: とは、ホットスポット キーがある時点で期限切れになることを指します。この時点で、このキーに対して多数の同時リクエストがあり、大量のリクエストがデータベースにヒットします。

    キャッシュ ブレークダウンは少し似ています。実際、それらの違いは、キャッシュ クラッシュは、データベースに過剰な負荷がかかっているか、さらにはダウンしていることを意味することです。キャッシュ ブレークダウンは、DB データベース レベルへの同時リクエストの数が単に多いだけです。ブレークダウンはキャッシュ スノーランのサブセットであると考えることができます。一部の記事では、この 2 つの違いは、ブレークダウンが特定のホット キー キャッシュを対象としているのに対し、Xuebeng は多くのキーを対象としている点であると考えられています。

    解決策は 2 つあります:

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

    5. ホット キーの問題とその解決方法

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

    ホットスポット キーのリクエストがサーバー ホストに送信されると、リクエスト量が特に多いため、ホスト リソースが不足したり、ダウンタイムが発生したりして、通常のサービスに影響を与える可能性があります。

    ホットスポット キーはどのように生成されますか?主な理由は 2 つあります。

    • ユーザーが消費するデータは、速報セール、話題のニュース、その他のシナリオなど、より多くの読み取りとより少ない書き込みが必要なシナリオなど、生成されるデータよりもはるかに大きいためです。
    • リクエストシャーディングが集中し単一のRediサーバーの性能を超え、例えば固定名のキーとハッシュが同一サーバーに落ちてしまうとインスタントアクセス量が膨大になりマシンのボトルネックを超えてしまい、ホットキーの問題を引き起こします。

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

    • どのホット キーがエクスペリエンスに基づいているかを決定する;
    • クライアント統計をレポートする;
    • サービス エージェント層にレポートする

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

    • Redis クラスターの拡張: シャード コピーを追加して読み取りトラフィックのバランスをとる;
    • ホット キーをさまざまなサーバーに配布する;
    • セカンダリ キャッシュを使用する、つまり、 JVM ローカル キャッシュにより、Redis 読み取りリクエストが削減されます。

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

    6.1 Redis の有効期限戦略

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

    スケジュールされた有効期限

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

    遅延有効期限

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

    定期的な有効期限

    一定の時間ごとに、特定の数のデータベースの有効期限切れディクショナリ内の特定の数のキーがスキャンされ、期限切れのキーがクリアされます。 。この戦略は、最初の 2 つの戦略の折衷案です。スケジュールされたスキャンの時間間隔と各スキャンの制限された消費時間を調整することにより、さまざまな状況下で CPU とメモリのリソース間の最適なバランスを実現できます。

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

    Redis は、

    遅延有効期限と定期有効期限の両方を使用します2 つの有効期限戦略を使用します。

    • 假设Redis当前存放30万个key,并且都设置了过期时间,如果你每隔100ms就去检查这全部的key,CPU负载会特别高,最后可能会挂掉。
    • 因此,redis采取的是定期过期,每隔100ms就随机抽取一定数量的key来检查和删除的。
    • 但是呢,最后可能会有很多已经过期的key没被删除。这时候,redis采用惰性删除。在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间并且已经过期了,此时就会删除。

    但是呀,如果定期删除漏掉了很多过期的key,然后也没走惰性删除。就会有很多过期key积在内存内存,直接会导致内存爆的。或者有些时候,业务量大起来了,redis的key被大量使用,内存直接不够了,运维小哥哥也忘记加大内存了。难道redis直接这样挂掉?不会的!Redis用8种内存淘汰策略保护自己~

    6.2 Redis 内存淘汰策略

    • volatile-lru:当内存不足以容纳新写入数据时,从设置了过期时间的key中使用LRU(最近最少使用)算法进行淘汰;
    • allkeys-lru:当内存不足以容纳新写入数据时,从所有key中使用LRU(最近最少使用)算法进行淘汰。
    • volatile-lfu:4.0版本新增,当内存不足以容纳新写入数据时,在过期的key中,使用LFU算法进行删除key。
    • allkeys-lfu:4.0版本新增,当内存不足以容纳新写入数据时,从所有key中使用LFU算法进行淘汰;
    • volatile-random:当内存不足以容纳新写入数据时,从设置了过期时间的key中,随机淘汰数据;。
    • allkeys-random:当内存不足以容纳新写入数据时,从所有key中随机淘汰数据。
    • volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的key中,根据过期时间进行淘汰,越早过期的优先被淘汰;
    • noeviction:默认策略,当内存不足以容纳新写入数据时,新写入操作会报错。

    7.说说Redis的常用应用场景

    • 缓存
    • 排行榜
    • 计数器应用
    • 共享Session
    • 分布式锁
    • 社交网络
    • 消息队列
    • 位操作

    7.1 缓存

    我们一提到redis,自然而然就想到缓存,国内外中大型的网站都离不开缓存。合理的利用缓存,比如缓存热点数据,不仅可以提升网站的访问速度,还可以降低数据库DB的压力。并且,Redis相比于memcached,还提供了丰富的数据结构,并且提供RDB和AOF等持久化机制,强的一批。

    7.2 排行榜

    当今互联网应用,有各种各样的排行榜,如电商网站的月度销量排行榜、社交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 计数器应用

    各大网站、APP应用经常需要计数器的功能,如短视频的播放数、电商网站的浏览数。这些播放数、浏览数一般要求实时的,每一次播放和浏览都要做加1的操作,如果并发量很大对于传统关系型数据的性能是一种挑战。Redis天然支持计数功能而且计数的性能也非常好,可以说是计数器系统的重要选择。

    7.4 共享Session

    如果一个分布式Web服务将用户的Session信息保存在各自服务器,用户刷新一次可能就需要重新登录了,这样显然有问题。实际上,可以使用Redis将用户的Session进行集中管理,每次用户更新或者查询登录信息都直接从Redis中集中获取。

    7.5 分布式锁

    几乎每个互联网公司中都使用了分布式部署,分布式服务下,就会遇到对同一个资源的并发访问的技术难题,如秒杀、下单减库存等场景。

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

    7.6 ソーシャル ネットワーク

    好き/嫌い、ファン、共通の友人/お気に入り、プッシュ、プルダウンの更新などはソーシャル ネットワーキングの重要な機能です通常、ソーシャルな Web サイトへのアクセスは多く、従来のリレーショナル データはこの種のデータの保存には適していませんが、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 はメモリベースの非リレーショナル K-V データベースです。メモリベースであるため、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 はコマンド リクエストを受け入れ、マスターからの書き込みコマンドを実行します。 buffer

    redis のバージョン 2.8 以降では、sync コマンドはシステム リソースを消費し、psync の方が効率的であるため、psync が sync の代わりに使用されています。

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

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

    ##9.2 センチネル モード

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

    Sentinel モード: 1 つ以上の Sentinel インスタンスで構成される Sentinel システム。すべての Redis マスター ノードとスレーブ ノードを監視でき、監視対象のマスター ノードが は、オフライン マスター サーバーの下のスレーブ ノードを新しいマスター ノード に自動的にアップグレードします。ただし、センチネル プロセスが Redis ノードを監視する場合、問題が発生する可能性があります (単一点の問題 )。そのため、複数のセンチネルを使用して Redis ノードを監視することができ、各センチネル間で継続的な通信が行われます。

    Sentinel Sentinel モード

    簡単に言えば、Sentinel モードには 3 つの機能があります。

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

    フェイルオーバー プロセスとは何ですか?

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

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

    • 各 Sentinel は、マスター、スレーブ、および既知の他の Sentinel インスタンスに 1 秒に 1 回程度メッセージを送信します。 PING コマンド。

    • PING コマンドに対する最後の有効な応答からの時間が、down-after-milliseconds オプションで指定された値を超えた場合、インスタンスは Sentinel によって主観的にオフラインとしてマークされます。

    • マスターが主観的オフラインとしてマークされている場合、マスターを監視しているすべてのセンチネルは、マスターが実際に主観的オフライン状態に入ったことを 1 秒に 1 回確認する必要があります。

    • 十分な数のセンチネル (設定ファイルで指定された値以上) が、マスターが指定された時間範囲内に実際に主観的オフライン状態に入ったことを確認すると、マスターはオフラインの目標としてマークされます。

    • 通常の状況では、各センチネルは、10 秒に 1 回、知っているすべてのマスターとスレーブに INFO コマンドを送信します。

    • マスターが Sentinel によって客観的にオフラインとしてマークされると、Sentinel がオフライン マスターのすべてのスレーブに INFO コマンドを送信する頻度が 10 秒に 1 回から 1 秒に 1 回に変更されます

    • マスターがオフラインであることに同意する十分なセンチネルがいない場合、マスターの客観的なオフライン ステータスは削除されます。マスターがセンチネルの PING コマンドに対して有効な応答を返した場合、マスターの主観的なオフライン ステータスは削除されます。オフラインステータスが削除されます。ステータスが削除されます。

    9.3 クラスター クラスター モード

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

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

    Redisクラスターは複数のノードで構成されます。

    各ノードはどのように通信しますか? ゴシッププロトコルを通じて!

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

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

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

    分散ストレージなので、クラスター クラスターで使用される分散アルゴリズムは

    Consistent Hash

    ですか?いいえ、ただし ハッシュ スロット スロット アルゴリズム

    スロット アルゴリズム

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

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

    Redis Cluster クラスターでは、16384 スロットに対応するノードが正常に動作していることを確認する必要があります。場合、担当するスロットも失敗し、クラスター全体が機能しなくなります。

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

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

    虽然数据是分开存储在不同节点上的,但是对客户端来说,整个集群Cluster,被看做一个整体。客户端端连接任意一个node,看起来跟操作单实例的Redis一样。当客户端操作的key没有被分配到正确的node节点时,Redis会返回转向指令,最后指向正确的node,这就有点像浏览器页面的302 重定向跳转。

    故障转移

    Redis集群实现了高可用,当集群内节点出现故障时,通过故障转移,以保证集群正常对外提供服务。

    redis集群通过ping/pong消息,实现故障发现。这个环境包括主观下线和客观下线

    主观下线: 某个节点认为另一个节点不可用,即下线状态,这个状态并不是最终的故障判定,只能代表一个节点的意见,可能存在误判情况。

    主观下线

    客观下线: 指标记一个节点真正的下线,集群内多个节点都认为该节点不可用,从而达成共识的结果。如果是持有槽的主节点故障,需要为该节点进行故障转移。

    • 假如节点A标记节点B为主观下线,一段时间后,节点A通过消息把节点B的状态发到其它节点,当节点C接受到消息并解析出消息体时,如果发现节点B的pfail状态时,会触发客观下线流程;
    • 当下线为主节点时,此时Redis Cluster集群为统计持有槽的主节点投票,看投票数是否达到一半,当下线报告统计数大于一半时,被标记为客观下线状态。

    流程如下:

    客观下线

    故障恢复:故障发现后,如果下线节点的是主节点,则需要在它的从节点中选一个替换它,以保证集群的高可用。流程如下:

    • 资格检查:检查从节点是否具备替换故障主节点的条件。
    • 准备选举时间:资格检查通过后,更新触发故障选举时间。
    • 发起选举:到了故障选举时间,进行选举。
    • 选举投票:只有持有槽的主节点才有票,从节点收集到足够的选票(大于一半),触发替换主节点操作

    10. 使用过Redis分布式锁嘛?有哪些注意点呢?

    分布式锁,是控制分布式系统不同进程共同访问共享资源的一种锁的实现。秒杀下单、抢红包等等业务场景,都需要用到分布式锁,我们项目中经常使用Redis作为分布式锁。

    选了Redis分布式锁的几种实现方法,大家来讨论下,看有没有啥问题哈。

    • 命令setnx + expire分开写
    • setnx + value值是过期时间
    • set的扩展命令(set ex px nx)
    • set ex px nx + 校验唯一随机值,再删除

    10.1 命令setnx + expire分开写

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

    如果执行完setnx加锁,正要执行expire设置过期时间时,进程crash掉或者要重启维护了,那这个锁就“长生不老”了,别的线程永远获取不到锁啦,所以分布式锁不能这么实现。

    10.2 setnx + value值是过期时间

    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(),最终只能有一个客户端加锁成功,但是该客户端锁的过期时间,可能被别的客户端覆盖。

    10.3:set的扩展命令(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(&#39;get&#39;,KEYS[1]) == ARGV[1] then 
       return redis.call(&#39;del&#39;,KEYS[1]) 
    else
       return 0
    end;
    ログイン後にコピー

    这种方式比较不错了,一般情况下,已经可以使用这种实现方式。但是存在锁过期释放了,业务还没执行完的问题(实际上,估算个业务处理的时间,一般没啥问题了)。

    11. 使用过Redisson嘛?说说它的原理

    分布式锁可能存在锁过期释放,业务没执行完的问题。有些小伙伴认为,稍微把锁过期时间设置长一些就可以啦。其实我们设想一下,是否可以给获得锁的线程,开启一个定时守护线程,每隔一段时间检查锁是否还存在,存在则对锁的过期时间延长,防止锁过期提前释放。

    当前开源框架Redisson就解决了这个分布式锁问题。我们一起来看下Redisson底层原理是怎样的吧:

    只要线程一加锁成功,就会启动一个watch dog看门狗,它是一个后台线程,会每隔10秒检查一下,如果线程1还持有锁,那么就会不断的延长锁key的生存时间。因此,Redisson就是使用Redisson解决了锁过期释放,业务没执行完问题。

    12. 什么是Redlock算法

    Redis一般都是集群部署的,假设数据在主从同步过程,主节点挂了,Redis分布式锁可能会有哪些问题呢?一起来看些这个流程图:

    如果线程一在Redis的master节点上拿到了锁,但是加锁的key还没同步到slave节点。恰好这时,master节点发生故障,一个slave节点就会升级为master节点。线程二就可以获取同个key的锁啦,但线程一也已经拿到锁了,锁的安全性就没了。

    为了解决这个问题,Redis作者 antirez提出一种高级的分布式锁算法:Redlock。Redlock核心思想是这样的:

    搞多个Redis master部署,以保证它们不会同时宕掉。并且这些master节点是完全相互独立的,相互之间不存在数据同步。同时,需要确保在这多个master实例上,是与在Redis单实例,使用相同方法来获取和释放锁。

    我们假设当前有5个Redis master节点,在5台服务器上面运行这些Redis实例。

    RedLock的实现步骤:如下

    • 1.获取当前时间,以毫秒为单位。
    • 2.按顺序向5个master节点请求加锁。客户端设置网络连接和响应超时时间,并且超时时间要小于锁的失效时间。(假设锁自动失效时间为10秒,则超时时间一般在5-50毫秒之间,我们就假设超时时间是50ms吧)。如果超时,跳过该master节点,尽快去尝试下一个master节点。
    • 3.客户端使用当前时间减去开始获取锁时间(即步骤1记录的时间),得到获取锁使用的时间。当且仅当超过一半(N/2+1,这里是5/2+1=3个节点)的Redis master节点都获得锁,并且使用的时间小于锁失效时间时,锁才算获取成功。(如上图,10s> 30ms+40ms+50ms+4m0s+50ms)
    • 如果取到了锁,key的真正有效时间就变啦,需要减去获取锁所使用的时间。
    • 如果获取锁失败(没有在至少N/2+1个master实例取到锁,有或者获取锁时间已经超过了有效时间),客户端要在所有的master节点上解锁(即便有些master节点根本就没有加锁成功,也需要解锁,以防止有些漏网之鱼)。

    简化下步骤就是:

    • 按顺序向5个master节点请求加锁
    • 根据设置的超时时间来判断,是不是要跳过该master节点。
    • 如果大于等于三个节点加锁成功,并且使用的时间小于锁的有效期,即可认定加锁成功啦。
    • 如果获取锁失败,解锁!

    13. Redis的跳跃表

    跳跃表

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

      キャッシュの遅延二重削除
    • キャッシュの再試行メカニズムの削除
    • Read biglog はキャッシュを非同期的に削除します
    14.1 二重削除が遅延していますか?

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

    #遅延二重削除プロセス

      #最初にキャッシュを削除します
    1. 次にデータベースを更新します
    2. しばらく (1 秒など) スリープして、再度キャッシュを削除します。
    一時的に眠るのに通常どれくらいかかりますか?全部1秒ですか?

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

    このソリューションは悪くありません。スリープ期間中 (たとえば、わずか 1 秒) に限り、ダーティ データが存在する可能性がありますが、一般の企業はそれを受け入れます。しかし、

    2 番目のキャッシュの削除が失敗した場合はどうなるでしょうか キャッシュとデータベースのデータがまだ不整合である可能性がありますよね?キーに自然な有効期限を設定して、自動的に期限切れになるようにしてはどうでしょうか?企業は有効期限内にデータの不整合を受け入れる必要がありますか?それとも他にもっと良い解決策があるのでしょうか?

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

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

    #キャッシュ削除の再試行 トライアルプロセス

    データベース更新リクエストの書き込み
    1. 何らかの理由でキャッシュの削除に失敗しました
    2. 削除に失敗したキーを入力してくださいメッセージ キューに追加します。
    3. メッセージ キューからメッセージを消費し、削除するキーを取得します。
    4. 削除キャッシュ操作を再試行します。
    5. 14.3 Biglog の読み取り非同期削除キャッシュ

    削除キャッシュ メカニズムを再試行します。確かに、多くの

    ビジネス コード侵入

    が発生します。実際、データベースのバイナリログを通じてキーを非同期的に削除するという方法で最適化することもできます。

    #mysql を例に挙げます

    Alibaba の運河を使用して binlog ログを収集し、MQ キューに送信できます

      その後、ACK メカニズムを通じてこの更新メッセージを確認して処理し、キャッシュを削除し、データ キャッシュの一貫性を確保します
    • 15. Redis はなぜ 6.0 以降マルチスレッドに変更されたのですか?

    Redis 6.0 より前は、Redis がソケットの読み取り、解析、実行、ソケットの書き込みなどのクライアント リクエストを処理する場合、それらはすべてシーケンシャルでシリアルなメイン スレッドによって処理されていました。 「シングルスレッド」と呼ばれます。

      Redis6.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 でのハッシュの競合に対処する方法
    • Redis は K-V インメモリ データベースであり、グローバル ハッシュを使用してすべてのキーと値を保存します。ペアです。このハッシュ テーブルは複数のハッシュ バケットで構成されています。ハッシュ バケットのエントリ要素には、
    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からすでに登場していましたが、最終的にredis通信プロトコルの標準となったのはredis2.0になってからです。

    RESP には、主に

    シンプルな実装、高速な解析速度、優れた可読性 という利点があります。

    20. ブルーム フィルター

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

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

    ブルームフィルターの原理は何ですか? 集合 A があり、A には n 個の要素があるとします。 k ハッシュ ハッシュ 関数を使用して、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 に変更します。

    ## 次に、ハッシュ 1 (d3) も 2 に等しいと仮定して、

    d3

    をマッピングします。また、添字を 2 グリッド添字 1 に設定します。

    したがって、要素 dn がセット A に含まれるかどうかを確認する必要があります。Hash1 (dn) によって取得されるインデックス添字を計算するだけで済みます。それが 0 である限り、つまりこれは次のことを意味します。要素

    はセット A

    にありません。インデックスの添字が 1 の場合はどうなりますか?その場合、要素 は A の要素

    である可能性があります。ご覧のとおり、d1 と d3 によって取得される添字の値は両方とも 1 である場合もあれば、他の数値にマッピングされている場合もあります。ブルーム フィルターには

    欠点 があります: ハッシュ False が存在します。衝突による陽性 、判断に誤りがあります。 このエラーを減らす方法?

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

    Hash2(d1)=6、Hash2(d3)=8 と仮定して、別の Hash2ハッシュ マッピング関数を追加します。そうでない場合は競合しません。次のように:

    エラーが発生した場合でも、ブルーム フィルターが store を完了していないことがわかります。 data は、一連のハッシュ マップ関数を使用して位置を計算し、バイナリ ベクトルを埋めます。 数値が非常に大きいの場合、ブルーム フィルターは非常に低いエラー率で多くのストレージ スペースを節約でき、非常にコスト効率が高くなります。

    現在、ブルーム フィルターを適切に実装するオープン ソース クラス ライブラリが存在します。たとえば、Google の Guava クラス ライブラリ 、Twitter の Algebird クラス ライブラリなど、簡単に入手できるか、Redis 独自のクラス ライブラリに基づいています。ライブラリ ビットマップが独自のデザインを実装することも可能です。

    プログラミング関連の知識について詳しくは、プログラミング ビデオをご覧ください。 !

    以上がRedis の典型的な面接の 20 の質問と回答の要約 (共有)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:csdn.net
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート