一. 什么是Pipeline
在传统的请求-响应模式中,客户端与服务器之间的通信流程如下:
- 客户端发送一个命令到服务器。
- 服务器接收命令并执行。
- 服务器将执行结果返回给客户端。
- 客户端接收结果后,发送下一个命令
在这种传统的模式下,每个命令都需要经历完整的 RTT,这在高延迟网络环境下会导致显著的性能瓶颈。
Redis Pipeline 是一种批量执行命令的技术,允许客户端在不等待服务器响应的情况下,一次性发送多个命令到 Redis 服务器。它通过消除或减少网络往返次数(Round-Trip Time, RTT),能够显著提高命令执行的吞吐量,客户端可以将多个命令打包发送,服务器则依次执行这些命令并将结果返回给客户端,从而有效地提升了网络利用率和整体性能。
Pipeline 工作流程可以总结成下面 5个步骤:
打包命令: 客户端将多个 Redis 命令按照特定的格式打包成一个请求包。
发送命令: 将打包好的请求一次性发送给 Redis 服务器。
执行命令: Redis 服务器按顺序执行接收到的所有命令。
接收响应: 服务器将所有命令的执行结果按顺序返回给客户端。
解析响应: 客户端解析接收到的响应,并将结果对应到各个命令。
这种方式通过减少网络往返次数,有效降低网络延迟对性能的影响,特别适合于需要执行大量 Redis 命令的高并发场景。
尽管 Pipeline带来了性能的提升,但它也有一些缺点:
1.资源消耗: 发送大量命令一次性执行,可能会消耗较多的服务器资源,导致 Redis 其他操作的响应时间增加。
2.错误处理复杂: 在批量执行命令时,单个命令的错误处理可能变得复杂,需要逐一检查每个命令的执行结果。
3.顺序依赖: 如果命令之间存在顺序依赖,Pipeline 的批量执行需要确保正确的命令顺序。
4.不支持事务功能: Pipeline 只是批量执行命令的工具,不具备事务的原子性和隔离性
二. Pipeline与非Pipeline性能对比
下面用Jedis客户端来对比Pipeline与非Pipeline模式下查询Hash类型数据性能。
import com.utils.DataImportToRedisUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import java.util.List;public class RedisPipeLineTest {public static void main(String[] args) {Jedis jedis = new Jedis("192.168.47.130", 6379);jedis.auth("Admin123!");jedis.select(0);int execTimes = 10000;long start = System.currentTimeMillis();for (int i = 0; i < execTimes; i++) {long ipScore = DataImportToRedisUtils.ipv4ToLong("1.0.32.0");String blockedIps = jedis.hget("blockedIps", "52.228.155.178");}long end = System.currentTimeMillis();System.out.println("the jedis total time is:" + (end - start));Pipeline pipe = jedis.pipelined(); // 先创建一个 pipeline 的链接对象long start_pipe = System.currentTimeMillis();for (int i = 0; i < execTimes; i++) {pipe.hget("blockedIps", "52.228.155.178");}List<Object> res = pipe.syncAndReturnAll(); // 获取所有的 responselong end_pipe = System.currentTimeMillis();System.out.println("the pipe total time is:" + (end_pipe - start_pipe));}
}
运行结果:
the jedis total time is:3920
the pipe total time is:45
从运行结果可以看出,Pipeline比非Pipeline性能可以提升不少