管道(pipeline)
管道(pipeline)
Redis管道(pipeline)本身并不是Redis服务器直接提供的技术,这个技术本质上是有客户端提供的,跟服务器没有什么直接关系。
Redis消息交互
当我们使用客户端对Redis进行一次操作时,客户端将请求传送给服务器,服务器处理完毕后,在将响应回复给客户端。这需要花费一个网络数据包来回的时间。
如果连续执行多个指令,那就会花费多个网络数据包来回的时间。
回到客户端代码层, 客户端是经历了写->读->写->读四个操作才完整执行了两条指令。
如果调整读写顺序,改成写->写->读->读,这两个指令同样可以完成。
两个连续的写操作和两个连续的读操作总共只会花费一次网络来回,就好比连续的write操作合并了, 连续的read操作也合并了一样。
这就是管道操作的本质, 服务器根本没有任务区别对待, 还是收到一条消息, 执行一条消息, 回复一条消息的正常流程。客户端通过对管道中的指令列表改变读写顺序就可以大幅节省IO时间。管道指令越多,效果越好。
深入理解管道本质
深入分析一个请求交互的操作流程, 真实的情况是很复杂度, 要经过网络协议栈, 这个需要深入内核了。
上图是一个完整的请求交互流程图。
1、客户端进程调用write将消息写到操作系统内核为套接字分配的发送缓冲send buffer。
2、客户端操作系统内核将发送缓冲的内容发送到网卡, 网卡硬件将数据通过网际路由送到服务器网卡。
3、服务器操作系统内核将网卡数据放到内核为套接字分配的接受缓冲recv buffer。
4、服务器进程调用read从接收缓冲中取出消息进行处理。
5、服务器调用write将响应消息写到内核为套接字分配的发送缓冲send buffer。
6、服务器操作系统内核将发送缓冲的内容发送到网卡, 网卡硬件将数据通过网际路由送到客户端的网卡。
7、客户端操作系统内核将网卡数据放到内核为套接字分配的接受缓冲recv buffer。
8、客户端进程调用read从接收缓冲中提取消息返回给上层业务逻辑处理。
9、结束
我们开始以为write操作是要等到对方接收到消息才会返回, 但实际上不是这样的。write操作只负责将数据写到本地操作系统内核的发送缓冲然后就返回了。 剩下的事交给操作系统内核异步将数据发送到目标机器。 但是如果发送缓冲满了, 那么就需要等待缓冲空出空闲空间来,这个就是写操作的IO操作的真正耗时。
我们开始以为read操作时从目标数据拉取数据,但实际上不是这样的。read操作只负责将本地操作系统内核的接收缓冲中取出来就了事了。 但是如果缓冲是空的,那么就需要等数据到来, 这个就是读操作IO操作的真正耗时。
所以对于value = redis.get(key)这样一个简单的请求来说, write操作几乎没有耗时, 直接写道发送缓冲就返回,而read就会比较耗时了, 因为它要等待消息经过网络路由到目标机器处理后的响应消息, 再回到当前的内核读取缓冲才可以返回。这才是一个网络来回的真正开销。
而对于管道来说, 连续的write操作根本没有耗时, 之后第一个read操作会等待一个网络的来回开销, 然后所有的响应消息就都已经会送到内核的读缓冲了, 后续的read操作直接可以从缓冲拿到结果, 瞬间就返回了。
小结
这就是管道的本质,它并不是服务器的什么特性, 而是客户端通过改变了读写的顺序带来的性能的巨大提升。