原文  https://redis.io/topics/pipelining


【请求/应答协议和RTT】

Redis是使用“客户端-服务端”模式的TCP服务,也称为“请求/应答”协议

这意为一个请求要经过以下二个步骤完成:
- 客户端发送查询给服务端,然后从套接字读取服务端的应答,通常是阻塞方式
- 服务端执行命令,然后把应答发回给客户端

例如,以下四条命令序列:
Client: INCR X
Server: 1
Client: INCR X
Server: 2
Client: INCR X
Server: 3
Client: INCR X
Server: 4

客户端和服务端通过网络链路连接,网路有可能非常快(如回路接口),也可能非常慢(连接建立在有很多网络跳转的二台主机上),但无论如何都存在网络延迟,就是请求包从客户端传输到服务端,和应答包从服务端传回客户端的用时,称为“往返用时”(RTT, Round Trip Time),当客户端要连续执行很多请求时,如对一个List增加很多元素,或对一个DB创建很多Key,RTT对性能的影响是显而易见的,假设RTT时长为250毫秒(一个非常慢的因特网链路),即使服务端能够每秒处理10W个请求,在这种情况下也只能每秒最多处理4个请求,如果使用回路(loopback)接口,RTT可以非常短,但需要连续执行很多写操作时,还是会有一定耗时

幸好,有一个方法可以改善这种情况


【管道(Pipelining)】

让请求/应答服务端能够在即使客户端没有读取之前应答的情况下也能处理新的请求,这种方式可以发送多条命令给服务端而完全不用等待应答,只需在最后一步读取这些应答即可,这个被称为“管道(pepelining)”技术早已被广泛使用,例如很多POP3协议已经支持了这个特性,显著提升了从服务端下载新邮件的速度,Redis很早就已支持管道,所以无论在运行哪个Redis版本都可以使用

例如:
$ (echo -e "PING\r\nPING\r\nPING\r\n"; sleep 0.5) | nc localhost 6379
+PONG
+PONG
+PONG
如此,三条命令只有一次RTT开销,而不是每次请求都有

使用管道后,第一个例子的操作顺序将会如下:
Client: INCR X
Client: INCR X
Client: INCR X
Client: INCR X
Server: 1
Server: 2
Server: 3
Server: 4

重要说明:
在客户端使用管道发送命令期间,服务端会强制将应答存入内存队列,所以如果要用管道发送很多命令,最好以一个合理的条数将命令分批发送,例如1W条,读取应答,然后发送另1W条,再读取应答,如此循环,执行速度几乎相同,额外使用的内存是这1W条命令的应答队列大小


【管道 VS 脚本】

从Redis 2.6版本开始提供脚本(Scripting)功能,一些很多操作是在服务端的场景改用脚本后会更有效率,脚本的一个大优势是,它能在很小的延迟下读写数据,非常快的执行诸如读取、计算和写入,管道无助于这种场景,因为客户端在发送写命令之前,需要读命令的应答

有时,程序可能还想在管道里发送EVAL或EVALSHA命令,这是完全可行的,使用SCRIPT LOAD命令导入脚本,以保证EVALSHA命令能够调用它


09-09 16:49