Redis管道技术是一种通过同时发出多个命令来提高性能的技术,而无需等待对每个单独命令的响应。大多数Redis客户端都支持管道操作。

Redis pipelining is a technique for improving performance by issuing multiple commands at once without waiting for the response to each individual command. Pipelining is supported by most Redis clients.

客户端和服务器通过网络链接连接。 这样的链接可以非常快(环回接口),也可以非常慢(通过 Internet 建立的连接在两台主机之间有很多跳)。 无论网络延迟是多少,数据包从客户端传输到服务器,然后从服务器返回到客户端以携带回复都需要时间。

这个时间称为RTT(往返时间)。 当客户端需要连续执行许多请求时(例如,将许多元素添加到同一个列表,或使用许多键填充数据库),很容易看出这会如何影响性能。 例如,如果 RTT 时间为 250 毫秒(在 Internet 上的链接非常慢的情况下),即使服务器能够每秒处理 10 万个请求,我们也将能够每秒最多处理四个请求。

如果使用的接口是环回接口,则RTT要短得多(例如我的主机报告ping 127.0.0.1 0,044毫秒),但如果您需要连续执行多次写入,它仍然很多。

Redis管道

可以实现请求/响应服务器,以便即使客户端尚未读取旧响应,它也能够处理新请求。 这样就可以在不等待回复的情况下向服务器发送多个命令,最后一步读取回复。

这称为管道,并且是一种广泛使用了几十年的技术。 例如,许多 POP3 协议实现已经支持此功能,大大加快了从服务器下载新电子邮件的过程。

Redis 从很早的时候就支持管道,所以无论你运行的是什么版本,你都可以在 Redis 中使用管道。 这是使用原始 netcat 实用程序的示例:

$ (printf "PING\r\nPING\r\nPING\r\n"; sleep 1) | 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

注意:当客户端使用管道发送命令时,服务器将被迫使用内存对回复进行排队。 因此,如果您需要使用管道发送大量命令,最好将它们分批发送,每个批次都包含合理的数量,例如 10k 命令,读取回复,然后再次发送另外 10k 命令,依此类推。 速度几乎相同,但使用的额外内存将达到将这些 10k 命令的回复排队所需的最大数量。

这不仅仅是RTT的问题

管道不仅仅是一种减少与往返时间相关的延迟成本的方法,它实际上极大地提高了在给定 Redis 服务器中每秒可以执行的操作数量。 这是因为,在不使用流水线的情况下,从访问数据结构和生成回复的角度来看,为每个命令提供服务非常便宜,但从进行套接字 I/O 的角度来看,这是非常昂贵的 哦。 这涉及调用 read() 和 write() 系统调用,这意味着从用户态到内核态。 上下文切换是一个巨大的速度损失。

使用管道时,通常使用单个 read() 系统调用读取许多命令,并通过单个 write() 系统调用传递多个回复。 正因为如此,每秒执行的总查询数最初随着管道的延长几乎呈线性增加,最终达到不使用管道获得的基线的 10 倍.

管道与脚本

使用 Redis 脚本(在 Redis 2.6 或更高版本中可用),可以使用执行服务器端所需的大量工作的脚本更有效地解决许多流水线用例。 脚本的一大优势是它能够以最小的延迟读取和写入数据,使得读取、计算、写入等操作非常快(流水线在这种情况下无济于事,因为客户端在调用写入命令之前需要读取命令的回复)。

有时,应用程序可能还想在管道中发送 EVAL 或 EVALSHA 命令。 这是完全可能的,Redis 使用 SCRIPT LOAD 命令明确支持它(它保证可以调用 EVALSHA 而没有失败的风险)。

示例

假设我们现在有一个脚本commands.txt,

set k1 v1
set k2 v2
set k3 v3
set k4 v4

可以通过 cat commands.txt | redis-cli --pipe


werbenhu
1 声望0 粉丝

Hi there, I'm Werben Hu 🚀.