Redis()—— Pipeline

3.3.1 Pipeline概念
Redis客户端执行一条命令分为如下四个过程:
1) 发送命令
2) 命令排队
3) 命令执行
4) 返回结果
其中1) +4) 称为Round Trip Time( RTT, 往返时间) 。
Redis提供了批量操作命令( 例如mget、 mset等) , 有效地节约RTT。 但大部分命令是不支持批量操作的, 例如要执行n次hgetall命令, 并没有mhgetall命令存在, 需要消耗n次RTT。 Redis的客户端和服务端可能部署在不同的机器上。 例如客户端在北京, Redis服务端在上海, 两地直线距离约为1300公里, 那么1次RTT时间=1300×2/( 300000×2/3) = 13毫秒( 光在真空中传输速度为每秒30万公里, 这里假设光纤为光速的2/3) , 那么客户端在1秒内大约只能执行80次左右的命令, 这个和Redis的高并发高吞吐特性背道而驰。
Pipeline( 流水线) 机制能改善上面这类问题, 它能将一组Redis命令进行组装, 通过一次RTT传输给Redis, 再将这组Redis命令的执行结果按顺序返回给客户端, 图3-5为没有使用Pipeline执行了n条命令, 整个过程需要n次RTT。

图3-6为使用Pipeline执行了n次命令, 整个过程需要1次RTT。Pipeline并不是什么新的技术或机制, 很多技术上都使用过。 而且RTT在不同网络环境下会有不同, 例如同机房和同机器会比较快, 跨机房跨地区会比较慢。 Redis命令真正执行的时间通常在微秒级别, 所以才会有Redis性能瓶颈是网络这样的说法。
redis-cli的--pipe选项实际上就是使用Pipeline机制, 例如下面操作将set hello world和incr counter两条命令组装:
echo -en '*3\r\n$3\r\nSET\r\n$5\r\nhello\r\n$5\r\nworld\r\n*2\r\n$4\r\nincr\r\n$7\r\ncounter\r\n' | redis-cli --pipe

但大部分开发人员更倾向于使用高级语言客户端中的Pipeline, 目前大部分Redis客户端都支持Pipeline。

Jedis支持Pipeline特性, 我们知道Redis提供了mget、 mset方法, 但是并没有提供mdel方法, 如果想实现这个功能, 可以借助Pipeline来模拟批量删除, 虽然不会像mget和mset那样是一个原子命令, 但是在绝大数场景下可以使用。 下面代码是mdel删除的实现过程。
注意:这里为了节省篇幅, 没有写try catch finally, 没有关闭jedis。
public void mdel(List<String> keys) {
    Jedis jedis = new Jedis("127.0.0.1");
    // 1)生成pipeline对象
    Pipeline pipeline = jedis.pipelined();
    // 2)pipeline执行命令, 注意此时命令并未真正执行
    for (String key : keys) {
        pipeline.del(key);

    }

    // 3)执行命令

    pipeline.sync();
}
说明如下:
·利用jedis对象生成一个pipeline对象, 直接可以调用jedis.pipelined( ) 。
·将del命令封装到pipeline中, 可以调用pipeline.del( String key) , 这个方法和jedis.del( String key) 的写法是完全一致的, 只不过此时不会真正的执行命令。
·使用pipeline.sync( ) 完成此次pipeline对象的调用。
除了pipeline.sync( ),还可以使用pipeline.syncAndReturnAll( ) 将pipeline的命令进行返回, 例如下面代码将set和incr做了一次pipeline操作,并顺序打印了两个命令的结果:
    Jedis jedis = new Jedis("127.0.0.1");
    Pipeline pipeline = jedis.pipelined();
    pipeline.set("hello", "world");
    pipeline.incr("counter");
    List<Object> resultList = pipeline.syncAndReturnAll();
    for (Object object : resultList) {
        System.out.println(object);
    }
输出结果为:
OK
1 3.3.2 性能测试
表3-1给出了在不同网络环境下非Pipeline和Pipeline执行10000次set操作的效果, 可以得到如下两个结论:
·Pipeline执行速度一般比逐条执行要快。
·客户端和服务端的网络延时越大, Pipeline的效果越明显。

注意因测试环境不同可能得到的具体数字不尽相同, 本测试Pipeline每次携带100条命令。

3.3.3 原生批量命令与Pipeline对比
可以使用Pipeline模拟出批量操作的效果,但是在使用时要注意它与原生批量命令的区别, 具体包含以下几点:
· 原生批量命令是原子的, Pipeline是非原子的。
·原生批量命令是一个命令对应多个key, Pipeline支持多个命令。
·原生批量命令是Redis服务端支持实现的, 而Pipeline需要服务端和客户端的共同实现。
3.3.4 最佳实践
Pipeline虽然好用, 但是每次Pipeline组装的命令个数不能没有节制, 否则一次组装Pipeline数据量过大, 一方面会增加客户端的等待时间, 另一方面会造成一定的网络阻塞, 可以将一次包含大量命令的Pipeline拆分成多次较小的Pipeline来完成。
Pipeline只能操作一个Redis实例, 但是即使在分布式Redis场景中, 也可以作为批量操作的重要优化手段, 具体细节见第11章
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值