Redis究竟是单线程还是多线程?

​Redis是单线程的,在以前那就是真理,现在再说这句话,估计得有人来跟咱好好聊聊了。索性咱就来看看Redis到底是啥样呢?

Reactor式(反应器模式)

1传统阻塞IO模型

聊反应器模式前,我们先看⼀下传统阻塞IO模型的处理⽅式。

在传统阻塞IO模型中,由⼀个独⽴的Acceptor线程来监听客户端的连接,当有客户端请求过来时,它会为客户端分配⼀个新的线程来进行处理,当同时有多个请求过来,服务端对应的就会分配相应数量的线程,这样一来就会导致CPU频繁进行上下文切换操作,从而导致资源浪费。

有的连接请求过来之后不做任何事情,但服务端还会分配对应的线程,这样就造成不必要的线程开销,好比我们去餐厅吃饭,拿着菜单看了半天发现不合胃口,然后就走了。这段时间等我们点菜的服务员就相当于⼀个对应的线程,我们要点菜这个行为可以看作⼀个连接请求。

同时,每次建⽴连接后,当线程调⽤读写⽅法时,线程会被阻塞,直到有数据可读可写,在此期间线程不能做其它事情。接着上面的例⼦,我们出去转了⼀圈发现还是这家最好吃,回到这家餐厅⼜拿着菜单看了半天,服务员也在旁边等客户点完菜为⽌。这个过程中服务员什么也不能做,只能这么⼲等着,这个过程相当于阻塞。

像上面这样,每来⼀个请求就要分配⼀个线程,并且还得阻塞地等线程处理完。有的请求还只是过来连接下,啥也不干,还得给分配⼀个线程,这得对服务器资源要求那得多高,万一与高并发场景不期而遇了,那彻底疯了,不过对于连接数目比较⼩的的固定架构倒是可以考虑,大家参考下。

2伪异步IO

我们可能接触或者了解过⼀种通过线程池优化的解决方案,意思就是采用线程池和任务队列的方式来处理请求,被称作伪异步IO模型。

过程就是,当有客户端接⼊时,将客户端的请求封装成⼀个task投递到后端线程池中来处理。线程池维护⼀个消息队列和多个活跃线程,对消息队列中的任务进⾏处理。

这种解决方案,避免了为每个请求创建⼀个线程导致的线程资源耗尽问题,但说到底它的底层架构还是基于同步阻塞模型。假如线程池内的所有线程都阻塞了,对于更多请求就无法响应。所以这种模式会限制最⼤连接数,并不能从根本上解决问题。

咱们接着上面的例子来,餐厅⽼板在经营了⼀段时间后,顾客多了,原本店⾥的5个服务员⼀对⼀服务的话根本来不及。于是⽼板采⽤线程池的⽅式,也就是服务员服务完⼀个客⼈后⽴刻去服务另⼀个。

这时就会发现,有的客⼈点菜特别慢,服务员就得等到客⼈点完为⽌。如果5个客⼈都点的特别慢的话,那这5人就得⼀直等下去,导致其余的顾客陷于没有⼈服务的状态,这就是我们上边所说的线程池所有线程都被阻塞的情况。

 

3Reactor

Reactor模式的基本设计思想是基于I/O复用模型来实现的。

咱先看下I/O复⽤模型,它和传统IO多线程阻塞不同,I/O复⽤模型中多个连接共⽤⼀个阻塞对象,应⽤程序只需要在⼀个阻塞对象等待。当某个连接有新的数据可以处理时,操作系统通知应⽤程序,线程从阻塞状态返回,开始进⾏业务处理。

接着上面的例子,餐厅⽼板也发现了顾客点餐慢的问题,于是他采⽤另外一种方式,就是只留了⼀个服务员,当客⼈点餐的时,这个服务员就去招待别的客⼈,客⼈点好餐后直接喊服务员来进⾏服务。这时的顾客和服务员可以分别看作多个连接和⼀个线程。服务员阻塞在⼀个顾客那⾥,当有别的顾客点好餐后,他就⽴刻去服务其他的顾客。

我们了解reactor的设计思想后,再来看下reactor单线程的实现⽅案:

Reactor通过I/O复⽤程序监控客户端请求事件,收到事件后通过任务分派器进行分发。之后建⽴连接请求事件,是通过Acceptor处理,并建立对应的handler负责后续业务处理。其它非连接事件,Reactor会调⽤对应的handler完成read->业务处理->write处理流程,并将结果返回给客户端,以上操作都在⼀个线程里完成。

     

单线程时代

Redis是基于Reactor单线程模式来实现的。

IO多路复⽤程序接收到⽤户的请求后,全部打到⼀个队列⾥,交给文件分派器,对于后续的操作,和在reactor单线程实现方案看到的⼀样,整个过程都在一个线程里完成,因此Redis被称为是单线程的操作

对于单线程的Redis来说,基于内存且命令操作时间复杂度低,因此读写速率很快。

三、多线程时代

Redis6中引入了多线程,咱们都知道Redis单线程处理有着很快的速度,那为啥还要引⼊多线程呢?单线程的瓶颈在哪里?

我们先来看第二个问题,Redis中,单线程的性能瓶颈主要在⽹络IO操作上。也就是在读写请求调用执行会占用大部分CPU时间。这时我们要对一些大的键值对进⾏删除操作的话,在短时间内是删不完的,此时对于单线程来说就会阻塞后边的操作。

咱们想想Reactor模式中单线程的处理方式,在针对⾮连接事件,Reactor会调⽤对应的handler完成read->业务处理->write处理流程,这很明显这⼀步会造成性能上的瓶颈。Redis在设计上采⽤将⽹络数据读写和协议解析通过多线程的⽅式来处理,对于命令执⾏来说,仍然使⽤单线程操作。

四、总结一下

1、Reactor模式

(1)传统阻塞IO模型客户端与服务端线程1:1分配,不利于扩展。

(2)伪异步IO模型采用线程池,底层仍为同步阻塞,限制了最大连接数。

(3)Reactor通过I/O复用程序监控客户端请求,通过任务分派器进行分发。

2、单线程时代

(1)基于Reactor单线程模式实现,通过IO多路复⽤程序接收到⽤户的请求后,全部推送到⼀个队列⾥,交给⽂件分派器进⾏处理。

3、多线程时代

(1)单线程性能瓶颈主要在⽹络IO上。

(2)将⽹络数据读写和协议解析通过多线程的⽅式来处理,对于命令执⾏来说,仍然使⽤单线程操作。

以上所有仅代表个人认知,侵权请联系删除。

 

 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luyaran

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值