目录
1. redis 客户端-服务端模型的不足之处
2. redis 管道是什么?有什么好处?
3. 管道的使用场景
4. 管道使用的注意事项
众所周知,redis 是一个客户端-服务端的模型设计,客户端向服务端发送存储数据的请求共分为四步(发送命令——>命令排队——>命令执行——>返回结果),简单来说就是一问一答的交互方式,在等待返回结果的期间,通常是以阻塞的模式等待服务端响应。
举个例子,我使用 set 命令存放3个数据,实际上是与 redis 交互了三次。
127.0.0.1:6379> set user1 aaa
OK
127.0.0.1:6379> set user2 bbb
OK
127.0.0.1:6379> set user3 ccc
OK
首先:客户端发送 set user1 aaa 命令;
其次:该命令开始排队等待被执行;
然后:开始执行当前命令;
最后:客户端等待命令执行返回的结果;
这样做是有有个很大的缺点的,那就是频繁交互会比较耗费性能,每执行一条命令,就需要客户端和服务端进行一次交互,而且还会阻塞等待。一旦出现高并发的场景,每秒钟都会有上千上万次的数据请求,频繁调用系统的IO资源发送网络请求,会对系统资源造成浪费,redis 的性能就会因为频繁的交互而大打折扣。
也许有些朋友会说,我可以使用 mset 命令,一次完成上述散步存放操作,如下,使用 mset 命令就可以一次存放 user1,user2,user3 三条数据,降低了交互次数提高了效率。
但是,mset 命令只能用于 String 类型数据的存储,如果换成hset,就会报错,所以不难看出,mset 命令是有一定的局限性的。
127.0.0.1:6379> mset user1 aaa user2 bbb user3 ccc
OK
127.0.0.1:6379> mset user4 ddd hset k1 v1 k2 v2
(error) ERR wrong number of arguments for 'mset' command
所以,为了解决频繁数据交互导致 redis 性能下降,就有了管道(Pipeline)。
管道本身不难理解,像我们日常生活中遇到的水管,天然气管,都是用来传输特定的东西的。类比到 Redis 中,Redis管道就是用来批量传输命令的。
redis 管道的本质其实是一个队列,用一句话来说它可以批次处理多条命令;将两个,三个,甚至N个命令合成一个,一块打包交给 Redis 服务器,让 Redis 服务器一起执行这些命令。可以类似的理解为 Redis 原生批命令 "mset","mget" 。
管道的好处:
(1)将原本需要多步完成的数据交互操作一次完成,提高了数据的处理效率;
(2)显著减少了客户端与服务器之间的网络通信次数,尤其是对于需要执行大量命令的场景,能够极大的降低网络延迟带来的影响;
Pipeline主要适用于需要对 Redis 执行大量命令的操作,例如数据批量导入,大规模数据更新,复杂查询等。这些耗时操作如果不使用管道将会使整体执行时间显著增加,降低了服务器的响应速度。
对于涉及事务(translaction)的操作,虽然也可以使用Pipeline来打包命令,但需要注意的是,Pipeline不提供事务的原子性和一致性。如果有明确需求要确保一组命令作为一个原子单位执行,建议使用 Redis 提供的MULTI/EXEC 命令来开启事务。
(1)分批处理:虽然 Pipeline 能够显著提高命令的执行效率,但一次性发送的命令数量也不宜过大,否则可能会导致数据包过大进而对网络传输造成较大压力,但是,如果发送的请求量过于庞大,Redis 也是会分多批次发送的。举个例子,加入我现在有一个客户端连接,要发送30K条数据进行存储,Redis 无法将30K条数据一次性接收处理,这个时候就会将30K条数据分三次进行发送,每次发送10K,而 Redis 在处理过后,也会分三次进行返回告知数据已经处理完毕。
(2)响应顺序:Redis服务器会按接收到命令的顺序返回结果。即使在Pipeline中并发发送多个命令,客户端接收到的响应也将按照命令发送的顺序排列。
(3)使用方式:实际开发过程中,由于内存,网络等各种因素的影响,管道的使用方式也是灵活多变的。常见的有服务端攒批和客户端攒批两种方式。
服务端攒批,这种也可以直接借助于服务端提供的事务支持指令来完成。
客户端存储大量数据操作,批次发送给服务端,服务端会按照顺序在短时间内处理大量请求。
(4)故障处理:如果Pipeline中的某个命令执行失败(如语法错误、key不存在等),后续命令通常仍会继续执行。错误信息会包含在相应命令的响应中,客户端可以根据这些信息判断哪些命令执行成功,哪些失败。
(5)异步操作:Redis 管道中的所有命令是在服务器端按顺序执行的,但客户端与服务器之间的通信是批量进行的,客户端可以在发送完一批命令后立刻开始处理其他任务,无需等待每个命令的单独响应。这种异步处理方式可以更好地利用客户端的计算资源,提高整体应用程序的并发性能。