简介
从接触Redis到现在,一直被它的单线程问题困扰,这对于一个苛求原理的我来说是种折磨,今天吃饭途中看了几篇博客,茅塞顿开。
个人理解
redis分客户端和服务端,一次完整的redis请求事件有多个阶段(客户端到服务器的网络连接-->redis读写事件发生-->redis服务端的数据处理(单线程)-->数据返回)。平时所说的redis单线程模型,本质上指的是服务端的数据处理阶段,不牵扯网络连接,这是理解redis单线程的第一步。接下来,针对不同阶段分别阐述个人的一些理解。
1:客户端到服务器的网络连接
首先,客户端和服务器是socket通信方式,socket服务端监听可同时接受多个客户端请求,这点很重要,如果不理解可先记住。注意这里可以理解为本质上与redis无关,这里仅仅做网络连接,或者可以理解为,为redis服务端提供网络交互api。
假设建立网络连接需要30秒(为了更容易理解,所以时间上扩大了N倍)
2:redis读写事件发生并向服务端发送请求数据
首先确定一点,redis的客户端与服务器端通信是基于TCP连接(不懂去看,基础很重要),第一阶段仅仅是建立了客户端到服务器的网络连接,然后才是发生第二阶段的读写事件。
完成了上一个阶段的网络连接,redis客户端开始真正向服务器发起读写事件,假设是set(写)事件,此时redis客户端开始向建立的网络流中送数据,服务端可以理解为给每一个网络连接创建一个线程同时接收客户端的请求数据。
假设从客户端发数据,到服务端接收完数据需要10秒。
3:redis服务端的数据处理
服务端完成了第二阶段的数据接收,接下来开始依据接收到的数据做逻辑处理,然后得到处理后的数据。数据处理可以理解为一次方法调用,带参调用方法,最终得到方法返回值。不要想复杂,重在理解流程。
假设redis服务端处理数据需要0.1秒
3:数据返回
这一阶段很简单,当reids服务端数据处理完后 就会立即返回处理后的数据,没什么特别需要强调的。
假设服务端把处理后的数据回送给客户端需要5秒。
那么什么是Reids的单线程
第一阶段说过,redis是以socket方式通信,socket服务端可同时接受多个客户端请求连接,也就是说,redis服务同时面对多个redis客户端连接请求,而redis服务本身是单线程运行。
假设,现在有A,B,C,D,E五个客户端同时发起redis请求,A优先稍微一点点第一个到达,然后是B,C,D,E依次到达,此时redis服务端开始处理A请求,建立连接需要30秒,获取请求数据需要10秒,然后处理数据需要0.1秒,回送数据给客户端需要5秒,总共大概需要45秒。也就是说,下一个B请求需要等待45秒,这里注意,也许这五个几乎同时请求,由于socket可以同时处理多个请求,所以建立网络连接阶段时间差可忽略,但是在第二阶段,服务端需要什么事都不干,坐等10秒中,对于CPU和客户端来说是无法忍受的。所以说单线程效率非常,非常低,但是正是因为这些类似问题,Redis单线程本质上并不是如此运行。接下来讨论redis真正的单线程运行方式。
客户端与服务端建立连接交由socket,可以同时建立多个连接(这里应该是多线程/多进程),建立的连接redis是知道的(为什么知道,去看socket编程,再次强调基础很重要),然后redis会基于这些建立的连接去探测哪个连接已经接收完了客户端的请求数据(注意:不是探测哪个连接建立好了,而是探测哪个接收完了请求数据),而且这里的探测动作就是单线程的开始,一旦探测到则基于接收到的数据开始数据处理阶段,然后返回数据,再继续探测下一个已经接收完请求数据的网络连接。注意,从探测到数据处理再到数据返回,全程单线程。这应该就是所谓的redis单线程。至于内部有多复杂我们无需关心,我们追求的是理解流程,苛求原理,但不能把内脏都挖出来。
从探测到接受完请求数据的网络连接到最终的数据返回,服务器只需要5.1秒,这个时间是我放大N倍后的数据,实际时间远远小于这个,可能是5.1的N万分之一时间,为什么这么说,因为数据的处理是在本地内存中,速度有多快任你想象,最终的返回数据虽然牵扯到网络,但是网络连接已经建立,这个速度也是非常非常快的,只是比数据处理阶段慢那么一点点。因此单线程方式在效率上其实并不需要担心。