redis专题系列10 -- redis管道技术剖析
mhr18 2024-11-10 09:51 17 浏览 0 评论
Redis是典型的C/S(客户端-服务端模型)架构,底层传输遵循TCP协议,那么一条请求的完整过程如下:
1.客户端通过socket发送请求到服务端,服务端处理请求,客户端阻塞
2.客户端读取socket从服务端的返回结果
即每个命令执行的总时间=客户端发送时间+服务器处理时间+一次网络来回的时间
根据测试,如果客户端和服务端不在一台主机的情况下,网络开销在命令的执行总时间占到很大比重,如果网络不稳定的场景下,一次网络来回的时间是不固定的,在这种情况下,redis的性能很大程度上取决于网络开销。如果我们的服务器每秒可以处理10万条请求,而网络开销是250毫秒,那么实际上每秒钟只能处理4个请求。最暴力的优化方法就是使客户端和服务器在一台物理机上,这样就可以将网络开销降低到1ms以下。但是实际的生产环境我们并不会这样做。而且即使使用这种方法,当请求非常频繁时,这个时间和服务器处理时间比较仍然是很长的。
Redis管道技术的诞生
为了应对这种情况,redis很早就引入了Pipelining技术,即一次性发送多条指令,并一次性返回处理的结果,类似于数据库中的批处理。这样只需要一次网络开销,速度就会得到明显的提升。管道技术其实已经非常成熟并且得到广泛应用了,例如POP3协议由于支持管道技术,从而显著提高了从服务器下载邮件的速度。
在Redis中,如果客户端使用管道发送了多条命令,那么服务器就会将多条命令放入一个队列中,这一操作会消耗一定的内存,所以管道中命令的数量并不是越大越好(太大容易撑爆内存),而是应该有一个合理的值。
深度理解redis一次交互的过程
管道并不只是用来网络开销延迟的一种方法,它实际上是会提升Redis服务器每秒操作总数的。在解释原因之前,需要更深入的了解Redis命令处理过程。
一次完整的详细交互过程如下:
客户端进程调用write()把消息写入到操作系统内核为Socket分配的send buffer中
操作系统会把send buffer中的内容写入网卡,网卡再通过网关路由把内容发送到服务器端的网卡
服务端网卡会把接收到的消息写入操作系统为Socket分配的recv buffer
服务器进程调用read()读取消息然后进行处理
处理完成后调用write()把返回结果写入到服务器端的send buffer
服务器操作系统再将send buffer中的内容写入网卡,然后发送到客户端
客户端操作系统将网卡内容读到recv buffer中
客户端进程调用read()从recv buffer中读取消息并返回
现在我们把命令执行的时间进一步细分:
命令的执行时间 = 客户端调用write并写网卡时间+一次网络开销的时间+服务读网卡并调用read时间++服务器处理数据时间+服务端调用write并写网卡时间+客户端读网卡并调用read时间
这其中除了网络开销,花费时间最长的就是进行系统调用write()和read()了,这一过程需要操作系统由用户态切换到内核态,中间涉及到的上下文切换会浪费很多时间。
使用管道时,多个命令只会进行一次read()和wrtie()系统调用,因此使用管道会提升Redis服务器处理命令的速度,随着管道中命令的增多,服务器每秒处理请求的数量会线性增长。
管道VS脚本
对于管道的大部分应用场景而言,使用Redis脚本(Redis2.6及以后的版本)会使服务器端有更好的表现。使用脚本最大的好处就是可以以最小的延迟读写数据。
有时我们也需要在管道中使用EVAL和EVALSHA命令,这是完全有可能的。因此Redis提供了SCRIPT LOAD命令来支持这种情况。
管道和非管道性能测试案例
有人做过简单的测试,测试条件很宽松,数据仅供参考
测试环境配置:
备注:C/S部署在同一台主机上,服务端使用虚拟机运行,在虚拟机上安装了redis镜像,其他条件一致,使用的java代码,针对string数据类型的get,set操作,结果如下: