Timetombs

泛义的工具是文明的基础,而确指的工具却是愚人的器物

66h / 117a
,更新于 2024-10-29T22:15:38Z+08:00 by   c73aa5b

[Redis] pipelining

版权声明 - CC BY-NC-SA 4.0

1 问题分析

设想一下有这样一个场景。在一个RTT1为10ms的网络环境下,循环执行一个INCR xxkey的操作,假定redis-server每次耗时1ms。

当cleint发出INCR xxkey的命令后,要等11ms后才能收到响应。如果循环100次,则总共就需要1100ms。显然redis-server执行100次仅需要1ms*100=100ms,而消耗在RTT1上所需的时间为10ms*100=1000ms。网络延迟消耗了太多的时间。

redis的基本通信模型是request/response。即client1发送一个request1,然后等待server处理完后的response1。在这个期间,server也是处于阻塞状态的,也就是说这时你又来另外一个client2的request2,那么这个request2也需要等待server1处理完response1后才能继续处理。

2 解决方案

那么有没有办法改善上述的网络性能问题呢?当然是有的,有2种途径:

  1. 应用层面:提升redis-server处理性能,不过目前已经是1ms了,即使优化成0.1ms,那么最终变成了10.01ms,改善也不明显。
  2. 网络层面:因为网络延迟占据了11ms中的10ms,占比最大,所以优化它的效果是最好的。这里也有2种途径:
    1. 优化网络,降低网络延迟。比如把10ms优化到6ms。
    2. 在一次request中塞进去多条命令。比如把100条命令按照顺序放在一个request中发给server,server则按照顺序在一个response中包含100个处理结果。

如何优化网络这里我们不关注,这里重点关注的是redis提供的pipelining2功能(在一次request中塞进去多条命令)。在网络延迟和server处理能力不变的前提下,我们一次性发送100个命令,这时的时间总和就是1ms*100+100ms=200ms,从而大大的改善了client网络性能和效率。

和HTTP/1.1中的pipelining是类似的技术。

pipelining2功能需要redis-server端的支持,但是无序单独的命令来开启,client一次性把多个命令一块发出去就是了。常用到的client库通常都直接支持,比如Jedis[^jedis]:

Pipeline pipline = jedis.pipelined();
for (int i = 0; i < 100; i++) {
    pipline.get(i + "");
}
List<Object> resultList = pipline.syncAndReturnAll();

需要注意的是这些指令直接应该是没有依赖关系的,比如后一条指令依赖前一条指令的结果这种肯定是不行的,这时就需要用到Lua Script3了。此外也不是原子性的,它仅仅只是一个批量操作(当然每个命令本身还是原子性的)。

3 参考

上一篇 : [Redis] data type
下一篇 : [Redis] expire