パイプライン:ノードで使用して、Redisのパフォーマンスの問題を解決できます

序文

まず、Redisクライアントとサーバー間の相互作用モデルを見てみましょう。

次のように結論付けることができます。

1. Redisは、リクエストとレスポンスに基づく同期リクエストサービスです

2.クライアントがデータパケットをサーバーに送信してから、サーバーが応答データをクライアントに送り返します。これには一定の時間がかかります。この時間は、ラウンドトリップ時間RTT(ラウンドトリップ時間)と呼ばれます。

クライアントが連続して多くの要求を実行する必要がある場合、ラウンドトリップ時間がシステムパフォーマンスに影響することは簡単にわかります。

例:ラウンドトリップ時間RTTが250msの場合、Redisサーバーが1秒あたり1000リクエストを処理できる場合でも、1秒あたり最大4リクエストしか処理できません。

Redisは、上記のユースケースのパフォーマンスを向上させることができるパイプラインメソッドを提供します。以下を参照してください。

Redisパイプライン相互作用モデル

クライアントが最初に実行されたコマンドをバッファ(メモリ)に書き込み、最後に一度にRedisを送信することがわかります。

パイプラインはコマンドのバッチをパッケージ化してサーバーに送信します。サーバーは実行後にコマンドをパッケージ化して順番に返します。これにより、頻繁な対話型ラウンドトリップの時間が短縮され、パフォーマンスが向上します。

基本的な使用法

Pipeline pipeline =jedis.pipelined();
// 循环添加 1000个元素
for(int i = 0; i < 1000; i++){
    pipeline.rpush("rediskey", i + "");
        }
//执行 
pipeline.sync()
复制代码

それでも使い方はとても簡単です

パイプラインの本質

リクエストのやり取りのプロセスを深く分析しますが、実際の状況は非常に複雑です。

上の図は、完全なリクエストインタラクションフローチャートです。

  1. クライアントはwriteを呼び出して、オペレーティングシステムカーネルによってソケットに割り当てられた送信バッファにメッセージを書き込みます。
  2. システムカーネルはバッファの内容をネットワークカードに送信し、ネットワークカードハードウェアはデータをサーバーのネットワークカードにルーティングします
  3. サーバーネットワークカードは、カーネルによってソケットに割り当てられた受信バッファーにデータを配置します。
  4. サーバーはreadを呼び出して、処理のために受信バッファーからメッセージをフェッチします
  5. サーバーはwriteを呼び出して、応答コンテンツを送信バッファーに送信します
  6. サーバーカーネルは、ルーティングを介してバッファのコンテンツをクライアントのネットワークカードに送信します
  7. クライアントカーネルは、ネットワークカード内のデータを受信バッファに入れます。
  8. クライアントは、バッファからデータを読み取るためにreadを呼び出します

要約する

相手がメッセージを受信するまで書き込み操作は戻らないと思い始めましたが、そうではありません。

書き込みIO操作の実際の時間

書き込み操作は、ネイティブオペレーティングシステムカーネルの送信バッファーにデータを書き込んでから戻ることのみを担当します。残りはオペレーティングシステムカーネルに任されて、データをターゲットマシンに非同期的に送信します。ただし、送信バッファーがいっぱいの場合は、バッファーが空き領域を解放するのを待つ必要があります。これは、書き込み操作のIO操作に非常に時間がかかります。

読み取りIO操作の実際の時間

読み取り操作はターゲットマシンからデータをプルしていると考え始めましたが、そうではありません。読み取り操作は、ネイティブオペレーティングシステムカーネルの受信バッファーからデータを取得することのみを担当します。ただし、バッファが空の場合は、データが到着するのを待つ必要があります。これは、読み取り操作のIO操作に非常に時間がかかります。

value = redis.get(key)操作には時間がかかります

value = redis.get(key)などの単純なリクエストの場合、書き込み操作はほとんど時間がかからず、送信バッファーに直接戻りますが、メッセージがルーティングされるのを待つ必要があるため、読み取りには時間がかかります。ネットワークを介してターゲットマシンによって処理された応答メッセージは、現在のカーネル読み取りバッファに返送された場合にのみ返されます。

パイプラインのリアルタイム消費

パイプラインの場合、継続的な書き込み操作はまったく時間がかかりません。その後、最初の読み取り操作はネットワークラウンドトリップオーバーヘッドを待機し、その後、すべての応答メッセージがカーネルの読み取りバッファーに返送され、その後の読み取り操作が行われます。直接ですバッファから結果を取得し、すぐに返すことができます。

長所と短所

パイプラインの利点:

コマンドをパッケージ化して一度に実行することにより、パイプラインは、接続->コマンドの送信->結果を返すプロセスによって生成されるラウンドトリップ時間を節約し、I / O呼び出しの数を減らすことができます(ユーザーモードとカーネルモードの切り替え) )。

パイプラインのデメリット:

  • パイプラインメソッドはコマンドをパックして送信するため、パイプラインは各バッチにあまり多くのコマンドをパックできません。したがって、redisはすべてのコマンドを処理する前に、すべてのコマンドの処理結果をキャッシュする必要があります。これにはメモリ消費があります。
  • パイプラインはアトミック性を保証しません。コマンド実行中に1つのコマンドが異常な場合、他のコマンドは引き続き実行されます。したがって、アトミック性が必要な場合、パイプラインは推奨されません。
  • パイプラインは、一度に1つのRedisノードにのみ作用できます(理由は以下で説明します)

該当シーン

一部のシステムには高い信頼性要件がある場合があります。各操作は、操作が成功したかどうか、およびデータがredisに書き込まれたかどうかをすぐに知る必要があります。このシナリオは適切ではありません。

一部のシステムでは、データがバッチでredisに書き込まれ、一定の割合の書き込み失敗が許可される場合、このシナリオを使用できます。たとえば、一度に10,000レコードがredisに入り、2レコードが失敗する場合があります。問題ありません。後の段階で補償メカニズムがあります。スパン

たとえば、大量のメッセージを送信するシナリオでは、一度に10,000通のメッセージを送信し、それを最初のモードに従って実装すると、クライアントが要求に応答するのに長い時間がかかり、遅延が長すぎます。クライアントが5秒のタイムアウトを要求した場合、例外が確実にスローされ、大量のテキストメッセージのリアルタイム要件はそれほど高くないため、この時点でパイプラインを使用するのが最適です。

推奨事項

パイプラインは使いやすいですが、各パイプラインアセンブリのコマンド数を制御できないようにすることはできません。そうしないと、パイプラインアセンブリ内のデータ量が多すぎて、一方でクライアントの待機時間が長くなり、原因となります。一方、特定のネットワーク輻輳。多数のコマンドのパイプラインが複数の小さなパイプラインに分割されて完了します。

パイプラインストレステスト

Redisには、パイプラインテストに使用できるストレステストツールredis-benchmarkが付属しています。

ヒント:公式のredisベンチマークドキュメント:redis.io/topics/benc…

まず、通常のsetコマンドでストレステストを実行します。QPSは約5w/sです。

> redis-benchmark -t set -q
SET: 51975.05 requests per second
复制代码

単一のパイプライン内の並列リクエストの数を示すパイプラインオプション-Pパラメーターを追加します。以下のP=2を参照すると、QPSは9w/sに達します。

> redis-benchmark -t set -P 2 -q
SET: 91240.88 requests per second
复制代码

P = 3をもう一度見ると、QPSは10w/sに達します。

SET: 102354.15 requests per second
复制代码

その他の問題

パイプラインが1つのRedisノードでのみ機能する、つまりパイプラインをクラスターモードで使用できないのはなぜですか?

Redisクラスターのキースペースは16384スロット(スロット)に分割されており、各マスターノードは16384ハッシュスロットの一部を処理する責任があります。

特定のredisコマンドは、キーに従ってスロット(slot)を計算し、スロットに従って特定のノードのredisで操作を実行します。次のように:

master1(slave1): 0~5460
master2(slave2):5461~10922
master3(slave3):10923~16383
复制代码

クラスタは3つのマスターノードで構成され、そのうちmaster1には0 5460スロット、master2には5461 10922スロット、master3には10923〜16383スロットが割り当てられます。

パイプラインは複数のコマンドをバッチで実行し、各コマンドはキーに従ってスロット(CRC16.getSlot(key))を操作し、次にスロットに従って特定のノードでコマンドを実行する必要があります。パイプライン操作では、現在サポートされていない複数のノードのRedis接続が使用されます

ヒント:Redisクラスターの知識がわからない場合は、redis.io / topics/clus…を参照してください。

パイプラインと、mgetやmsetなどのバッチ操作の違いは何ですか?

mgetとmsetもパイプラインに似ており、一度に複数のコマンドを実行して一度に送信するため、ネットワーク時間を節約できます。

比較は次のとおりです。

  • mset、mget操作はRedisキューのアトミック操作であり、パイプラインはアトミック操作ではありません
  • mset、mgetは複数のキーと値のペアに対応するコマンドを操作し、パイプラインは複数のコマンドです
  • mset、mgetはサーバーによって実装され、パイプラインはサーバーとクライアントによって完了されます

パイプラインとトランザクションの違いは何ですか?

パイプラインはRTT時間に焦点を合わせ、トランザクションは一貫性に焦点を合わせます

  • パイプラインはリクエストであり、サーバーは順番に実行され、1回戻り、トランザクションは複数回リクエストし(MULTIコマンド+他のnコマンド+ EXECコマンド、つまり少なくとも2つのリクエスト)、サーバーは順番に実行され、1回戻ります。
  • クラスタモードでは、パイプラインを使用する場合、スロットは正しい必要があります。そうでない場合、サーバーはスロットxxxにredirecredのエラーを返します。同時に、トランザクション内のコマンドが想定されているため、トランザクションの使用はお勧めしません。はマスターAで実行され、マスターBでも実行され、Aは成功し、Bは何らかの理由で失敗するため、データに一貫性がありません。これは分散トランザクションに似ており、絶対的な一貫性を保証することはできません。

パイプラインにはコマンドの数に制限がありますか?

制限はありませんが、パッケージ化されたコマンドが多すぎることはできません。メモリの消費量が多くなります。

Pipelineがパッケージ化して実行するのに適切なコマンドはいくつありますか?

公式のRedisドキュメントを照会します。公式ドキュメントの説明によると、推奨されるバッチは10kです(注:これは参照値です。実際のビジネス状況に応じて調整してください)。

パイプラインをバッチで実行すると、他のアプリケーションは読み書きできなくなりますか?

RedisはマルチチャネルI/O多重化モデルとノンブロッキングIOを採用しているため、Pipelineがバッチで書き込む場合、特定の範囲内の他の読み取り操作に影響を与えません。

やっと

面接や仕事に役立つ実用的な技術記事を毎日共有する

参考:

公式ウェブサイト:redis.io/topics/pipe…

本:Redisの深い冒険:コア原則と応用実践


著者:プログラマーDuan Fei
リンク:https://juejin.cn/post/7089081484958679077
出典:RareEarthNuggets
著作権は著者に帰属します。商用の再版については、著者に連絡して許可を求め、非商用の再版については、出典を示してください。

おすすめ

転載: blog.csdn.net/wdjnb/article/details/124459618