java pipeline 实现_【死磕 Redis】- 理解 pipeline 管道 - Java 技术驿站-Java 技术驿站...

Redis Pipeline 是为了解决网络传输和IO调用效率低下的问题,它允许客户端一次性发送多条命令,减少RTT和IO次数。通过对比单个命令执行与Pipeline的性能测试,显示Pipeline能显著提高性能,尤其是在大量命令执行时。尽管Pipeline与批量命令有相似之处,但Pipeline不保证命令执行的原子性,适合不同类型的命令组合执行。
摘要由CSDN通过智能技术生成

原文出处:Java 技术驿站 『chenssy』

在前面博客中小编提到过 Redis 性能瓶颈主要是网络,主要原因就在于 Redis 执行命令的时间通常在微妙级别。正常情况下,我们执行一条 Redis 命令流程要经过如下几个步骤:

客户端发送 Redis 命令,阻塞等待 Redis 应答

Redis 接收到命令,执行命令

应答,客户端收到响应信息

136d4d5312b76ae6ba9a11a69fc19e29.png

其中 1 、3 称之为一次 RTT(Round Trip Time)。在这种情况下,如果同时执行大量命令,那当前命令需要等待上一条命令应答完成后才会执行,这个过程不仅仅只有多次 RTT,还有频繁的调用系统 IO,发送网络请求,如下图:

d6284990b0a7360767d2c9801c0f8806.png

把大量的时间消耗在来回路上,真正办事的时间就只有一点点,这种做法是非常不明智且低效的,为了解决这种低效的做法,pipeline 出现了,它允许客户端一次性发送多条命令,减少 RTT 和 IO 的调用次数(IO 调用涉及到用户态到内核态之间的切换)。如下图:

4fd23d6e83db5631a50720e3b4915dfd.png

实现原理

要支持 pipeline,除了需要 Redis 服务端支持,也需要各个客户端的支持,例如 jedis 就对 pipeline 提供了很好的支持。对于服务端来说,所需要的就是能够处理客户端通过一个 TCP 连接发送的多个命令,对多个命令进行排队,执行。而客户端,则需要将多个命令缓存起来,待缓冲区满了就发送,同时还需要处理 Redis 的应答。

pipeline 不是什么新鲜技术,很多技术都使用过,它能提供性能的核心就在于:它能将一组 Redis 命令进行组装,通过一次 RTT 传输给 Redis,同时再将这组命令的执行结果按照顺序返回给客户端。将原来一组命令多次 RTT 以及 IO 交互变成了一组 RTT 和 IO 交互,大大减少了网络传输时间和 IO 调用的时间。

下图是一次请求交互的流程(图片来自:《Redis 深度历险:核心原理与应用实践》),

695ea5a90693e803cb8d78240c8e140b.png

需要注意的是,在使用 pipeline 的过程中需要控制其中命令的大小,如果一次组装的命令过多,则会增加造成一定的网络阻塞,也会增加客户端的等待时间。

性能压测

Redis 提供了一个压测工具 redis-benchmark,该工具可以进行 pipeline 测试。如下:

3cd81e457653d1887e319c3140dcaec7.png

-P 参数:它表示单个管道内并行的请求数量

与批量的比较

pipeline 的批次提交命令与 Redis 的批量命令有很多相似之处,可以利用 pipeline 模拟出批次的效果,但是他们还是存在一些不同之处:

pipeline 可以一次性发送多个不同的命令,例如 set、get、而批量这是一次一个命令,只不过这一次对应的是多个 key 而已。

pipeline 只是将多个命令一起发出去而已,不保证这些命令的执行是原则性,而批量则是原子性的,他需要保证整个操作的正确性,避免中途出错而导致最后产生的数据不一致。

与单个命令的性能测试

public static void main(String[] args) throws IOException {

Jedis jedis = new Jedis("127.0.0.1");

Pipeline pipeline = jedis.pipelined();

long pipelineBegin = System.currentTimeMillis();

for(int i = 0 ; i < 100000 ; i++){

pipeline.set("pipeline:test_"+i,i + "");

}

//获取所有的 response

pipeline.sync();

long pipelineEnd = System.currentTimeMillis();

System.out.println("the pipeline is :" + (pipelineEnd - pipelineBegin));

long start = System.currentTimeMillis();

for (int i = 0; i < 100000; i++) {

jedis.set("jedis:test_"+i,i + "");

}

long end = System.currentTimeMillis();

System.out.println("the jedis is:" + (end - start));

}

2effa15c23de0b73bba4b53d6bc47b50.png

从图中可以看出,普通的方式与 pipeline 性能相差 10 倍,从这里可以看出 pipeline 的性能之强悍。

参考

付磊:《Redis 开发与运维》

掘金小册子:Redis 深度历险:核心原理与应用实践

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值