Redis系列十:Pipeline详解

Redis系列十:Pipeline详解1 pipeline 出现的背景 redis 客户端执行一条命令分 4 个过程 发送命令 命令排队 命令执行 返回结果这个过程称为 Roundtriptim 简称 RTT 往返时间 mgetmset 有效节约了 RTT 但大部分命令 如 hgetall 并没有 mhgetall 不支持批量操作 需要消耗 N 次 RTT 这个时候需要 pipeline 来解决这个问题 2 pepeline 的性能

一、pipeline出现的背景:

redis客户端执行一条命令分4个过程:

 发送命令-〉命令排队-〉命令执行-〉返回结果 

这个过程称为Round trip time(简称RTT, 往返时间),mget mset有效节约了RTT,但大部分命令(如hgetall,并没有mhgetall)不支持批量操作,需要消耗N次RTT ,这个时候需要pipeline来解决这个问题

二、pepeline的性能

1、未使用pipeline执行N条命令

在这里插入图片描述

2、使用了pipeline执行N条命令

在这里插入图片描述

3、两者性能对比

在这里插入图片描述
小结:这是一组统计数据出来的数据,使用Pipeline执行速度比逐条执行要快,特别是客户端与服务端的网络延迟越大,性能体能越明显。
下面贴出测试代码分析两者的性能差异:




 @Test public void pipeCompare() { Jedis redis = new Jedis("192.168.1.111", 6379); redis.auth("");//授权密码 对应redis.conf的requirepass密码 Map 
  
    data = new HashMap 
   
     (); 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 
    
      keys = redis.keys("key_*"); //将上面设值所有结果键查询出来 // 直接使用Jedis hgetall start = System.currentTimeMillis(); Map 
     
       > result = new HashMap 
      
        >(); 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(); } 
       
      
     
    
  

在这里插入图片描述

三、原生批命令(mset, mget)与Pipeline对比

1、原生批命令是原子性,pipeline是非原子性

(原子性概念:一个事务是一个不可分割的最小工作单位,要么都成功要么都失败。原子操作是指你的一个业务逻辑必须是不可拆分的. 处理一件事情要么都成功,要么都失败,原子不可拆分)

2、原生批命令一命令多个key, 但pipeline支持多命令(存在事务),非原子性

3、原生批命令是服务端实现,而pipeline需要服务端与客户端共同完成

四、Pipeline正确使用方式

使用pipeline组装的命令个数不能太多,不然数据量过大,增加客户端的等待时间,还可能造成网络阻塞,可以将大量命令的拆分多个小的pipeline命令完成。

1、Jedis中的pipeline使用方式

大家知道redis提供了mset、mget方法,但没有提供mdel方法,如果想实现,可以借助pipeline实现。

2、Jedis中的pipeline使用步骤:

  • 获取jedis对象(一般从连接池中获取)
  • 获取jedis对象的pipeline对象
  • 添加指令
  • 执行指令

测试类方法:

 @Test public void testCommond() { // 工具类初始化 JedisUtils jedis = new JedisUtils("192.168.1.111", 6379, ""); 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, ""); List 
  
    keys = new ArrayList 
   
     (); for (int i = 0; i < 100; i++) { keys.add("n" + i); } jedis.mdel(keys); System.out.println("after mdel the redis return ---------" + jedis.keys("*")); } 
    
  

JedisUtils下的mdel方法:

 / * 删除多个字符串key 并释放连接 * * @param keys* * @return 成功返回value 失败返回null */ public boolean mdel(List 
  
    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; } 
  

使用pipeline提交所有操作并返回执行结果:

@Test public void testPipelineSyncAll() { // 工具类初始化 Jedis jedis = new Jedis("192.168.1.111", 6379); jedis.auth(""); // 获取pipeline对象 Pipeline pipe = jedis.pipelined(); pipe.multi(); pipe.set("name", "james"); // 调值 pipe.incr("age");// 自增 pipe.get("name"); pipe.discard(); // 将不同类型的操作命令合并提交,并将操作操作以list返回 List  list = pipe.syncAndReturnAll(); for (Object obj : list) { // 将操作结果打印出来 System.out.println(obj); } // 断开连接,释放资源 jedis.disconnect(); } 

五、redis事务

pipeline是多条命令的组合,为了保证它的原子性,redis提供了简单的事务。

1、redis的简单事务,

2、停止事务discard

在这里插入图片描述

3、命令错误,语法不正确,导致事务不能正常结束

在这里插入图片描述

4、运行错误,语法正确,但类型错误,事务可以正常结束

在这里插入图片描述

5、watch命令:

WATCH的机制是:在事务EXEC命令执行时,Redis会检查被WATCH的key,只有被WATCH的key从WATCH起始时至今没有发生过变更,EXEC才会被执行。如果WATCH的key在WATCH命令到EXEC命令之间发生过变化,则EXEC命令会返回失败。

小结:redis提供了简单的事务,不支持事务回滚

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/230002.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月16日 下午3:21
下一篇 2026年3月16日 下午3:22


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号