主题
Redis Pipeline 模式
Redis 客户端和服务端之间采用 TCP 协议进行通信,基于 Request/Response 的一问一答模式,即请求一次响应一次。
普通模式
在普通模式下,一条 Redis 命令的简要执行过程如下:
- 客户端发送一条命令给 Redis 服务端,阻塞等待服务端应答。
- Redis 服务端接收到命令,执行命令。
- Redis 服务端将结果返回给客户端。
完整请求的交互过程包括以下步骤:
- 客户端调用
write()
将消息写入操作系统为 socket 分配的 send buffer。 - 操作系统将 send buffer 中的内容发送到网卡,通过网关路由将内容发送到服务器网卡。
- 服务器网卡将接收到的消息写入操作系统为 socket 分配的 recv buffer。
- 服务器进程调用
read()
从 recv buffer 中读取消息并进行处理。 - 处理完成后,服务器调用
write()
将响应内容发送到 send buffer。 - 服务器将 send buffer 中的内容通过网卡发送到客户端。
- 客户端操作系统将网卡中的内容放入 recv buffer。
- 客户端进程调用
read()
从 recv buffer 中读取消息。
普通模式的问题
在普通模式下,执行大量命令时,每个命令都需要经历上述流程,导致以下问题:
- 每个命令的执行时间 = 客户端发送耗时 + 服务器处理耗时 + 服务器返回耗时 + 一个网络来回耗时(RTT)。
- RTT 不稳定且难以控制,受网络线路拥堵、跳数等因素影响。
- 频繁的 IO 系统调用(如
read()
和write()
)会增加开销,涉及用户态和内核态的切换。
Pipeline 模式
为了解决普通模式的性能问题,Redis 提供了 Pipeline 模式。Pipeline 允许客户端一次性发送多条命令,而无需等待每条命令的响应,服务端接收到所有命令后依次执行,并将结果打包后一次性返回给客户端。
Pipeline 的优点
- 减少 RTT:通过将多个命令打包为一次网络请求,减少了网络来回的次数。
- 减少 IO 调用次数:减少了频繁的
read()
和write()
系统调用,优化了性能。
基本使用
以下是 Pipeline 的 Java 使用示例:
java
Pipeline pipeline = jedis.pipelined();
for (int i = 0; i < 100; i++) {
pipeline.rpush("rediskey", i + "");
}
pipeline.sync();
Pipeline 的核心思想是:客户端将一组 Redis 命令组装后通过一次 RTT 发送给服务器,服务器按顺序执行命令并将结果一次性返回。
Pipeline 注意事项
虽然 Pipeline 能显著提升性能,但在使用时需要注意以下几点:
命令数量不宜过多:
- 客户端会将命令写入内存缓冲区,命令过多可能导致缓冲区溢出或占用过多内存。
- 官方推荐每次发送不超过 10,000 条命令,建议将大量命令拆分为多个小的 Pipeline。
仅支持单节点操作:
- Pipeline 一次只能运行在一个 Redis 节点上。在集群环境下,如果命令涉及多个节点,Pipeline 无法正常工作。
不保证原子性:
- Pipeline 仅是将多个命令打包发送,不保证所有命令作为一个整体执行。如果中间某条命令出错,剩余命令仍会继续执行。
Pipeline 与批量操作(如 MGET)的区别
适用场景:
- MGET 等批量操作适用于一个命令操作多个键值对。
- Pipeline 适用于多条不同命令的批量执行。
原子性:
- MGET 是一个原子操作。
- Pipeline 不保证原子性。
实现方式:
- MGET 是服务端实现。
- Pipeline 是客户端和服务端共同实现。
Pipeline 与事务的区别
请求方式:
- Pipeline 是一次请求,服务端顺序执行后一次性返回结果。
- 事务需要多次请求(先
MULTI
,再执行多个命令,最后EXEC
),服务端顺序执行后一次性返回结果。
关注点:
- Pipeline 关注减少 RTT 和 IO 调用。
- 事务关注一致性问题。
总结
Pipeline 是 Redis 提供的一种优化机制,通过减少网络往返和 IO 调用次数显著提升性能。但在使用时需要注意命令数量、节点限制和原子性问题。合理使用 Pipeline 可以在多命令执行场景下获得更好的性能表现。
TIP
原文作者: Max Fang
原文链接: http://www.immaxfang.com/redis-pipeline/
版权声明:本作品采用 署名-非商业性使用 4.0 国际 (CC BY-NC 4.0)进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。