ホームページ  >  記事  >  データベース  >  Redisコマンドの使用例分析

Redisコマンドの使用例分析

WBOY
WBOY転載
2023-05-30 15:46:52655ブラウズ

問題の原因

編集者が担当するアプリケーションは管理バックグラウンドアプリケーションであり、権限管理にはShiroフレームワークを使用しています複数のノードがあるため分散セッションを使用する必要があるため、Redisを使用していますセッション情報を保存するために使用されます。

Shiro は Redis ストレージ セッション コンポーネントを直接提供していないため、Afan は Github のオープン ソース コンポーネントである hiro-redis を使用する必要がありました。

Shiro フレームワークはセッションが有効かどうかを定期的に確認する必要があるため、Shiro の最下層は SessionDAO#getActiveSessions を呼び出してすべてのセッション情報を取得します。

そして hiro-redis はたまたま SessionDAO インターフェイスを継承しており、最下層は keys コマンドを使用してすべての Session を検索します## Redis に保存されます。#key。

public Set<byte[]> keys(byte[] pattern){
    checkAndInit();
    Set<byte[]> keys = null;
    Jedis jedis = jedisPool.getResource();
    try{
        keys = jedis.keys(pattern);
    }finally{
        jedis.close();
    }
    return keys;
}
問題の原因がわかれば、解決策は比較的簡単です。github で解決策を見つけて、

shiro-redis を最新バージョンにアップグレードします。

このバージョンでは、

hiro-redis は、keys の代わりに scan コマンドを使用して、この問題を解決します。

public Set<byte[]> keys(byte[] pattern) {
    Set<byte[]> keys = null;
    Jedis jedis = jedisPool.getResource();


    try{
        keys = new HashSet<byte[]>();
        ScanParams params = new ScanParams();
        params.count(count);
        params.match(pattern);
        byte[] cursor = ScanParams.SCAN_POINTER_START_BINARY;
        ScanResult<byte[]> scanResult;
        do{
            scanResult = jedis.scan(cursor,params);
            keys.addAll(scanResult.getResult());
            cursor = scanResult.getCursorAsBytes();
        }while(scanResult.getStringCursor().compareTo(ScanParams.SCAN_POINTER_START) > 0);
    }finally{
        jedis.close();
    }
    return keys;


}
問題は無事解決されましたが、アーフェンはまだ少し混乱していました。

keys コマンドによって他のコマンドの実行が遅くなるのはなぜですか?

なぜ

Keysコマンドクエリは非常に遅いのでしょうか?

Scan コマンドでは問題がないのはなぜですか?

Redis コマンド実行の原則

まず最初の質問、

keys 命令によって他のコマンドの実行が遅くなるのはなぜですか?

クライアントの観点から見ると、コマンドの実行は 3 つのステップに分かれています:

  1. コマンドの送信

  2. 実行コマンド

  3. 結果を返す

しかし、これはクライアントが考えているプロセスに過ぎませんが、実際には同時に、多くのクライアントが Redis にコマンドを送信する可能性がある Redis がシングルスレッド モデルを使用していることは誰もが知っています。

すべてのクライアント要求コマンドを同時に処理するために、Redis は内部でキューを使用してキューを実行します。

したがって、クライアントがコマンドを実行するには、実際には 4 つの手順が必要です。

  1. コマンドの送信

  2. コマンド キューイング

  3. コマンドの実行

  4. 結果を返す

Redis は単一スレッドでコマンドを実行するため、開始することしかできません。タスクをキューから取り出して順次実装します。

3 である限り、このプロセスはコマンドの実行が遅すぎるため、キュー内の他のタスクは待機する必要があります。外部クライアントにとって、Redis はブロックされているように見え、応答しません。

したがって、Redis プロセスを使用する場合は、長時間実行が必要な命令を実行しないでください。これにより、Redis がブロックされ、他の命令の実行に影響を与える可能性があります。

KEYS の原則

2 番目の質問に答え始めましょう。

Keys コマンド クエリはなぜ非常に遅いのでしょうか?

この質問に答える前に、Redis の基礎となるストレージ構造を思い出してください。

友達のことを知らなくても問題ありません。前回の記事「Alibaba Interviewer: Are you know with HashMap? オーケー、Redis 辞書について話しましょう!」を振り返ることができます。

keys コマンドは、指定されたパターン pattern に一致するすべての Redis キーを返す必要があります。この目的を達成するために、Redis は辞書 を走査する必要があります。 ht[ 0]ハッシュ テーブルの基礎となる配列。今回の複雑さは 『O(N)』 (N は Redis のキーの数) です。

Redis のキーの数が少なくても、実行速度は高速です。 Redis キーの数が徐々に増加し、数百万、数千万、さらには数億に達すると、実行速度が非常に遅くなります。

以下は、Ah Fen がローカルで行った実験です。lua スクリプトを使用して 10W のキーを Redis に追加し、

keys を使用してすべてのキーをクエリします。このクエリは約 10 秒間ブロックされます。 . .

eval "for i=1,100000  do redis.call('set',i,i+1) end" 0
ここでは、Afan は Redis をデプロイするために Docker を使用しているため、パフォーマンスは若干劣る可能性があります。

SCAN 原則

最後に 3 番目の質問を見てみましょう。なぜ

scan コマンドには問題がないのでしょうか?

これは、

scan コマンドがブラック テクノロジである 「カーソルベースの反復子」 を使用しているためです。

scan コマンドが呼び出されるたびに、Redis は新しいカーソルと特定の数のキーをユーザーに返します。次回も引き続き残りのキーを取得したい場合は、このカーソルを scan コマンドに渡して、前の反復プロセスを続行する必要があります。

簡単に言うと、

scan コマンドはページングを使用して Redis をクエリします。

次は、scan コマンドの反復プロセスの例です。

scan このコマンドは、カーソルを使用して完全なクエリを複数回に巧みに分割し、複雑さを軽減します。クエリの支出。

scan コマンドの時間計算量は keys と同じで、どちらも 「O(N)」 ですが、 scan このコマンドは少数のキーを返すだけでよいため、実行速度は非常に速くなります。

最後に、

scan コマンドは keys の欠点を解決しますが、他のいくつかの欠点も引き起こします。

  • 同じelement 複数回返される可能性があるため、アプリケーションに繰り返しの要素を処理する関数を追加する必要があります。

  • 反復プロセス中に、Redis に追加される要素または削除される要素が返される場合もあれば、返されない場合もあります。

上記の欠陥は​​、開発時に考慮する必要があります。

scan に加えて、redis には増分反復のための他のコマンドがいくつかあります:

  • sscan: use 反復に使用されます。現在のデータベース内のデータベース キー。 smembers ブロックされる可能性がある問題を解決するために使用されます。

  • #hscan コマンドは、ハッシュ キーを反復するために使用されます。 キー-value ペアは、可能性のあるブロッキング問題 hgetall を解決するために使用されます。

  • zscan: このコマンドは、順序付きセット内の要素 (要素メンバーと要素スコアを含む) を反復するために使用され、zrange を生成するために使用されます。 ブロッキングの問題が発生する可能性があります。

以上がRedisコマンドの使用例分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。