Redisのよくある面接問題のまとめと解析


面接の後端開発のポストでは、redisに関する質問がよく聞かれると信じています.Redisはキャッシュシステムの代表として理解する必要があります.仕事中でも就職面接中でも役立ちます.本稿ではredisの典型的な問題を詳しく紹介し、参考解答を提供します.作者のレベルが限られているため、いくつかの問題があるかもしれませんが、批判と指導を惜しまないことを歓迎します.文の中でネットユーザーのいくつかの資料を参考にして、ここでまず彼らが感謝します.本文の全文は約4000字で、読み終わったら10分ぐらいかかります.

よくある質問


Redisのパフォーマンスはなぜ高いのですか?単一スレッドのredisはどのようにマルチコアcpuマシンを利用しますか?Redisのキャッシュ淘汰ポリシー?Redisはどのようにデータを永続化しますか?Redisにはどのようなデータ構造がありますか?Redisクラスタにはどのような形式がありますか?大量のkeyとvalueの比較的小さいデータがあって、redisの中でどのように記憶して更にメモリを節約しますか?redisとDBのデータの一貫性を保証するにはどうすればいいですか?キャッシュの透過とキャッシュの雪崩をどのように解決しますか?redisで分散ロックを実現するにはどうすればいいですか?

質問リファレンスリスト


Redisのパフォーマンスはなぜ高いのですか?


Redisはkey-valueに格納されているnosqlデータベースで、以下の性質を有し、パフォーマンスに優れています.
  • はメモリ操作に完全に基づいており、ほとんどの操作がメモリで完了し、非常に効率的です.
  • 内部は多重I/O多重モデルを採用し、非ブロックIOである.Redisはシステムの状況に応じて効率的なIO多重モデル、例えばlinuxのepoll多重IO関数を優先的に呼び出す.
  • データ構造とデータ操作は簡単で、redis中のデータ構造は専門的に設計されている.
  • 処理要求モジュールは単一スレッドを採用し、不要なコンテキスト切替と競争条件を回避し、マルチプロセスやマルチスレッドによる切替も存在せず、CPUを消費し、各種ロックの問題を考慮する必要はなく、ロック解除ロック操作は存在せず、デッドロックが発生する可能性があるための性能消費はない.
  • 下位モデルより効率的な通信のためにredisは直接VMメカニズムを構築した.一般的なシステムがシステム関数を呼び出すと、移動や要求に一定の時間を浪費するからだ.

  • 単一スレッドのredisはどのようにマルチコアcpuマシンを利用しますか?


     Redisのデータ読み取りと処理性能は非常に強く、一般的なサーバ構成ではcpuはパフォーマンスのボトルネックではありません.Redisのパフォーマンスのボトルネックは主にメモリとネットワークに集中しています.例えば、1台の通常のLinuxマシン上で少なくとも50万の同時要求を処理することができる.したがって,redisコマンドはO(N),O(log(N))時間の複雑さが多く,cpuボトルネックはほとんど発生しない.ただし、マルチコアcpuを十分に使用する必要がある場合は、単一のサーバ上で複数のredisインスタンス(プライマリ・セカンダリ・デプロイ/クラスタ化デプロイ)を実行し、各redisインスタンスとcpuカーネルをバインドしてマルチコア・CPUを十分に利用する必要があります.

    Redisのキャッシュ淘汰ポリシー?


    Redisキャッシュ淘汰ポリシーは、Redisキーの期限切れ削除ポリシーと全く同じではなく、前者はRedisメモリが一定値を超えた場合(一般的にこの値は構成可能)に使用される淘汰ポリシーである.後者は定期削除+不活性削除の両方を組み合わせてメモリ淘汰を行う.キャッシュ・トーナメントには、次の6つのポリシーがあります.
  • volatile-lru:有効期限が設定されているデータの中から、最近最も使用されているデータを選択して淘汰します.
  • volatile-ttl:有効期限が設定されているデータの中から、有効期限が切れるデータを淘汰する.
  • volatile-random:有効期限が設定されているデータから任意にデータ淘汰を選択します.
  • allkeys-lru:データセットから最近最も使用されているデータを選択して淘汰します.
  • allkeys-random:データセットから任意にデータを選択して淘汰する.
  • no-enviction(駆逐):駆逐禁止データ
  • ここで6つのメカニズムに注意してください.volatileとallkeysは、有効期限が設定されているデータセットに対してデータを淘汰します.allkeysはすべてのデータセットからデータを淘汰し、後のlru、ttl、randomは3つの異なる淘汰戦略であり、no-envictionが回収しない戦略を加えます.

    Redisはどのようにデータを永続化しますか?


    Redisは、RDB方式とAOF方式の2つのデータ永続化方式をサポートする.前者は、構成されたルールに従ってメモリ内のデータをハードディスク(HDD)に永続化し、後者は書き込みコマンドを実行するたびにコマンドを記録します.2つの永続化方式は単独で使用できますが、通常は両方を組み合わせて使用します.1、RDB方式RDB方式の永続化はスナップショット方式で行われる.あるルールに合致すると、メモリ内のデータの全量が1部のコピーを生成してハードディスクに格納され、このプロセスは「スナップショット」と呼ばれます.たとえば、構成ルールに従ってスナップショットの自動処理を行います.
  • ユーザーはSAVE、BGSAVEコマンドを実行する.
  • FLUSHALLコマンドを実行します.
  • は、レプリケーションを実行する.

  • 2、AOF方式では、Redisを使用して非一時データを格納する場合、AOF持続化をオンにしてプロセス終了によるデータ損失を低減することが一般的である.AOFはRedisが実行する書き込みコマンドをハードディスクファイルに追加することができ、このプロセスは明らかにRedisの性能を低下させるが、ほとんどの場合、この影響は許容できる.また、より速いハードディスクを使用することでAOFの性能を向上させることができる.

    Redisにはどのようなデータ構造がありますか?


    Redisは、string(文字列)、hash(ハッシュ)、list(リスト)、set(集合)、zset(sorted set:秩序集合)の5つのデータ型をサポートします.

    Redisクラスタにはどのような形式がありますか?


    redisには、主従複製、哨兵モード、クラスタの3つのクラスタ方式があります.詳細は、「Redisクラスタ管理方式」を参照してください.

    大量のkeyとvalueの比較的小さいデータがあって、redisの中でどのように記憶して更にメモリを節約しますか?


    keyの数を大幅に減らすことでメモリの消費を低減できることが考えられます.実装:クライアントがパケットを介して大量のkeyを一定のポリシーに従ってhashオブジェクトのセットにマッピングすると、valueが小さいため、hashタイプのオブジェクトはメモリを消費する小さいziplist符号化を使用します.たとえば、100万個のキーが存在する場合、1000個のhashにマッピングし、各hashに1000個の要素を保存できます.

    redisとDBのデータの一貫性を保証するにはどうすればいいですか?


    ほとんどの場合、キャッシュポリシーは、読み取りキャッシュであり、読み取りデータベースが読み込まれずにキャッシュに同期します.問題が発生したシーンは、ライブラリを書き込み、キャッシュを削除するのにかかわらず、同時アクセス中です.キャッシュを削除してからライブラリを書くか、データが一致しない可能性があります1、同時では読み書きの順序が保証されていません.キャッシュを削除してもまだライブラリを書く時間がない場合は、別のスレッドが読み取りに来てキャッシュが空であることを発見したら、データベースに行ってキャッシュを読み取り、書き込みます.このとき、キャッシュは汚いデータです.2、先にライブラリを書いてからキャッシュを削除する前に、ライトライブラリのスレッドがダウンし、キャッシュを削除していないと、データが一致しない場合もあります.3.redisクラスタ、またはマスタースレーブモードであれば、ライトマスターリードは、redisレプリケーションに一定の時間遅延があるため、データの不一致を招く可能性がある.ダブル削除+タイムアウトはライブラリを書く前後でredisを行う.del(key)操作を行い、合理的なタイムアウト時間を設定します.このような最悪のケースは、タイムアウト時間内に不一致があることです.もちろん、このようなケースは極めて珍しく、サービスダウンタイムが原因である可能性があります.この場合、ほとんどのニーズを満たすことができます.もちろん、このポリシーはredisとデータベース・プライマリの同期にかかる時間を考慮する必要があります.したがって、2回目の削除前に500ミリ秒などの一定時間スリープすると、書き込み要求の時間が増加することは間違いありません.

    キャッシュの透過とキャッシュの雪崩をどのように解決しますか?


    キャッシュ雪崩:キャッシュが同じ時間に大面積の失効し、瞬時に要求がデータベースに完全に落下し、データベースが短時間で大量の要求に耐えて崩壊することを指す.ソリューション:キャッシュデータの有効期限をランダムに設定し、同じ時間に大量のデータが有効期限切れになることを防止します.一般的にコンカレント量が特に多くない場合、最も多く使用される解決策はロックキューです.各キャッシュデータに対応するキャッシュタグを追加し、キャッシュが失効したかどうかを記録し、キャッシュタグが失効した場合、データキャッシュを更新します.
    ≪キャッシュ・スルー|Cache Transfer|emdw≫:キャッシュとデータベースにないデータが、すべてのリクエストをデータベースに落とし、データベースが短時間で大量のリクエストに耐えて崩壊することを意味します.
    ソリューション
  • インタフェース層は、ユーザ認証検査などの検査を追加し、idは基礎検査を行い、id<=0の直接ブロックを行う.
  • キャッシュから取り出せないデータは、データベースからも取り出せません.この場合、key-valueペアをkey-nullと書くこともできます.キャッシュ有効時間は30秒などの短いポイントを設定できます(設定が長すぎると通常の状況でも使用できません).これにより、攻撃ユーザが同じidで暴力攻撃を繰り返すことを防止することができる
  • .
  • は、ブロンフィルタを用いて、存在する可能性のあるすべてのデータを十分なbitmapにハッシュし、存在しないデータがこのbitmapによってブロックされ、下位ストレージシステムに対するクエリー圧力
  • を回避する.

    redisで分散ロックを実現するにはどうすればいいですか?


    分散環境では複数の異なるスレッドが共有リソースにアクセスし、Javaなどの従来のロックメカニズムでは実現できません.この場合、分散環境での共有リソースの同期問題を解決するために、分散ロックを使用する必要があります.
    分散ロックが使用可能であることを確認するには、少なくともロックの実装が次の4つの条件を満たしていることを確認します.
  • 相互反発性.任意の時点において、ロック
  • を保持できるクライアントは1つのみである.
  • デッドロックは発生しません.ロックを保持している間にクラッシュし、アクティブにロックを解除しなくても、後続の他のクライアントがロックを追加できることを保証します.
  • はフォールトトレランスを有する.ほとんどのRedisノードが正常に動作している限り、クライアントはロックとロック解除を行うことができます.
  • ロックとロック解除は同じクライアントでなければなりません.クライアント自身は他の人のロックを解除することはできません.[1]
  • public class RedisTool {
    
        private static final String LOCK_SUCCESS = "OK";
        private static final String SET_IF_NOT_EXIST = "NX";
        private static final String SET_WITH_EXPIRE_TIME = "PX";
        private static final Long RELEASE_SUCCESS = 1L;
    
        /**
         *         
         * @param jedis Redis   
         * @param lockKey  
         * @param requestId     
         * @param expireTime     
         * @return       
         */
        public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
    
            String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
    
            if (LOCK_SUCCESS.equals(result)) {
                return true;
            }
            return false;
    
        }
     /**
         *       
         * @param jedis Redis   
         * @param lockKey  
         * @param requestId     
         * @return       
         */
        public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
    
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
    
            if (RELEASE_SUCCESS.equals(result)) {
                return true;
            }
            return false;
    
        }
    }
    

    参考文献[1]:Redis分散ロックの正確な実現方式
    私のCSDNブログに注目してください.あなたの行動は私に最大の励ましです.