1. パイプラインの外観の背景:
Redis には、コマンドを実行するための 4 つのプロセスがあります: コマンドの送信、コマンドのキューイング、コマンドの実行、および結果の返し;
このプロセスはラウンド トリップ タイム (ラウンド トリップ タイムの略で RTT) と呼ばれます。mget mset は RTT を効果的に節約しますが、ほとんどのコマンド (mhgetall ではなく hgetall など) はバッチ操作をサポートしていないため、 N 回の RTT を消費するため、この問題を解決するにはパイプラインが必要です。
2. パイプラインのパフォーマンス
1. N 個のコマンドの実行にパイプラインは使用されません
2. N 個のコマンドの実行にパイプラインが使用されますコマンド
3. 2 つのパフォーマンスの比較
概要: これは、Pipeline を使用した統計データのセットです。実行速度は 1 つずつ実行するよりも速く、特にクライアントとサーバー間のネットワーク遅延が大きいほど、パフォーマンスがより顕著になります。
2 つのパフォーマンスの違いを分析するためのテスト コードを以下に示します:
@Test public void pipeCompare() { Jedis redis = new Jedis("192.168.1.111", 6379); redis.auth("12345678");//授权密码 对应redis.conf的requirepass密码 Map<String, String> data = new HashMap<String, String>(); redis.select(8);//使用第8个库 redis.flushDB();//清空第8个库所有数据 // hmset long start = System.currentTimeMillis(); // 直接hmset for (int i = 0; i < 10000; i++) { data.clear(); //清空map data.put("k_" + i, "v_" + i); redis.hmset("key_" + i, data); //循环执行10000条数据插入redis } long end = System.currentTimeMillis(); System.out.println(" 共插入:[" + redis.dbSize() + "]条 .. "); System.out.println("1,未使用PIPE批量设值耗时" + (end - start) / 1000 + "秒.."); redis.select(8); redis.flushDB(); // 使用pipeline hmset Pipeline pipe = redis.pipelined(); start = System.currentTimeMillis(); // for (int i = 0; i < 10000; i++) { data.clear(); data.put("k_" + i, "v_" + i); pipe.hmset("key_" + i, data); //将值封装到PIPE对象,此时并未执行,还停留在客户端 } pipe.sync(); //将封装后的PIPE一次性发给redis end = System.currentTimeMillis(); System.out.println(" PIPE共插入:[" + redis.dbSize() + "]条 .. "); System.out.println("2,使用PIPE批量设值耗时" + (end - start) / 1000 + "秒 .."); //-------------------------------------------------------------------------------------------------- // hmget Set<String> keys = redis.keys("key_*"); //将上面设值所有结果键查询出来 // 直接使用Jedis hgetall start = System.currentTimeMillis(); Map<String, Map<String, String>> result = new HashMap<String, Map<String, String>>(); for (String key : keys) { //此处keys根据以上的设值结果,共有10000个,循环10000次 result.put(key, redis.hgetAll(key)); //使用redis对象根据键值去取值,将结果放入result对象 } end = System.currentTimeMillis(); System.out.println(" 共取值:[" + redis.dbSize() + "]条 .. "); System.out.println("3,未使用PIPE批量取值耗时 " + (end - start) / 1000 + "秒 .."); // 使用pipeline hgetall result.clear(); start = System.currentTimeMillis(); for (String key : keys) { pipe.hgetAll(key); //使用PIPE封装需要取值的key,此时还停留在客户端,并未真正执行查询请求 } pipe.sync(); //提交到redis进行查询 end = System.currentTimeMillis(); System.out.println(" PIPE共取值:[" + redis.dbSize() + "]条 .. "); System.out.println("4,使用PIPE批量取值耗时" + (end - start) / 1000 + "秒 .."); redis.disconnect(); }
3. ネイティブ バッチ コマンド (mset、mget) とネイティブ バッチ コマンドの比較パイプライン
1. ネイティブ バッチ コマンドはアトミックであり、パイプラインは非アトミックです。
(アトミック性の概念: トランザクションは、分割できない最小の作業単位であり、すべてが成功するかすべてが成功するかのいずれかです)失敗します。アトミック操作は、ビジネス ロジックを分割できない必要があります。1 つの処理は成功するか失敗するかのどちらかであり、アトムを分割することはできません)
2. ネイティブ バッチ コマンドは複数のキーをコマンドしますが、パイプラインは複数のコマンドをサポートします. (トランザクションが存在します)、非アトミック
3. ネイティブ バッチ コマンドはサーバー側で実装されますが、パイプラインはサーバーとクライアントによって一緒に完了する必要があります
4。パイプラインの正しい使い方
パイプラインを使用して組み立てられるコマンドの数が多すぎてはなりません。そうしないと、データ量が多すぎて、クライアントの待ち時間が長くなり、ネットワークの混雑が発生する可能性があります。多数のコマンドを複数の小さなパイプライン コマンドに分割して完了できます。
1. Jedis でパイプラインを使用する方法
ご存知のとおり、redis は mset メソッドと mget メソッドを提供しますが、mdel メソッドは提供しません。これを実現したい場合は、パイプラインを使用できます。それを達成するために。
2. Jedis でパイプラインを使用する手順:
jedis オブジェクトを取得します (通常は接続プールから取得します)
# # jedis オブジェクトのパイプライン オブジェクトを取得します
命令を追加します
命令を実行します
Testクラス メソッド: JedisUtils の
@Test public void testCommond() { // 工具类初始化 JedisUtils jedis = new JedisUtils("192.168.1.111", 6379, "12345678"); for (int i = 0; i < 100; i++) { // 设值 jedis.set("n" + i, String.valueOf(i)); } System.out.println("keys from redis return =======" + jedis.keys("*")); } // 使用pipeline批量删除 @Test public void testPipelineMdel() { // 工具类初始化 JedisUtils jedis = new JedisUtils("192.168.1.111", 6379, "12345678"); List<String> keys = new ArrayList<String>(); for (int i = 0; i < 100; i++) { keys.add("n" + i); } jedis.mdel(keys); System.out.println("after mdel the redis return ---------" + jedis.keys("*")); }
mdel メソッド:
/** * 删除多个字符串key 并释放连接 * * @param keys* * @return 成功返回value 失败返回null */ public boolean mdel(List<String> keys) { Jedis jedis = null; boolean flag = false; try { jedis = pool.getResource();//从连接借用Jedis对象 Pipeline pipe = jedis.pipelined();//获取jedis对象的pipeline对象 for(String key:keys){ pipe.del(key); //将多个key放入pipe删除指令中 } pipe.sync(); //执行命令,完全此时pipeline对象的远程调用 flag = true; } catch (Exception e) { pool.returnBrokenResource(jedis); e.printStackTrace(); } finally { returnResource(pool, jedis); } return flag; }
パイプラインを使用してすべての操作を送信し、実行結果を返す:
@Test public void testPipelineSyncAll() { // 工具类初始化 Jedis jedis = new Jedis("192.168.1.111", 6379); jedis.auth("12345678"); // 获取pipeline对象 Pipeline pipe = jedis.pipelined(); pipe.multi(); pipe.set("name", "james"); // 调值 pipe.incr("age");// 自增 pipe.get("name"); pipe.discard(); // 将不同类型的操作命令合并提交,并将操作操作以list返回 List<Object> list = pipe.syncAndReturnAll(); for (Object obj : list) { // 将操作结果打印出来 System.out.println(obj); } // 断开连接,释放资源 jedis.disconnect(); }
5.redis トランザクション
パイプラインは複数のコマンドの組み合わせであり、そのアトミック性を確保するために、redis は単純なトランザクションを提供します。
1. Redis の単純なトランザクション、
一緒に実行する必要があるコマンドのグループは、multi コマンドと exec コマンドの間に配置されます。ここで、multi はトランザクションの開始を表し、exec はトランザクションの開始を表します。取引の終了。
2. トランザクションの破棄を停止します
3. コマンドと構文が間違っているため、トランザクションは正常に終了しません
4. 実行エラー、構文は正しいですが、型が間違っています、トランザクションは正常に終了できます
5. watch コマンド:
watch を使用した後、マルチが失敗し、トランザクションが失敗します
WATCH のメカニズムは次のとおりです。トランザクション EXEC コマンドが実行されると、Redis は WATCH キーをチェックします。EXEC は、WATCH の開始以降に WATCH されているキーが変更されていない場合にのみ実行されます。 WATCH コマンドと EXEC コマンドの間で WATCH キーが変わると、EXEC コマンドは失敗を返します。
redis の詳細については、redis 入門チュートリアル 列に注目してください。
以上がRedisのパイプラインについて詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。