相信你必定不止一次见过Redis是单线程模式,不过说实话那只是个老版本,这个问题是一位老哥的大厂面试题,跟我分享了一下。想着本身就知道redis6.0之前一直都是单线程,到了6的版本才加入了多线程,还不是很清楚,在多方打听而且搜索之下总结了这篇文章。面试
1、问题概述 Redis 6.0 以后的版本抛弃了单线程模型这一设计,本来使用单线程运行的 Redis 也开始选择性使用多线程模型,乍一看Redis的做者这么牛,也逃不过“真香定律”,redis
Redis为何又引入了多线程?做者也逃不过“真香定理”? 仔细想一想,这个问题其实能够拆分,拆分为两个主要的问题:数据库
(1)为何 Redis 一开始选择单线程模型(单线程的好处)?服务器
(2)为何 Redis 在 6.0 以后加入了多线程(在某些状况下,单线程出现了缺点,多线程能够解决)?网络
其实,做者并非没有逃脱真香定理,而是随着时间的推移,出现的问题也愈来愈多,原来的设计确定就有些不合时宜,该作出改变就作出改变。OK,带着俩问题,咱们就来好好地分析一下。多线程
2、为何Redis一开始使用单线程 不论是单线程或者是多线程都是为了提高Redis的开发效率,由于Redis是一个基于内存的数据库,还要处理大量的外部的网络请求,这就不可避免的要进行屡次IO。好在Redis使用了不少优秀的机制来保证了它的高效率。那么为何Redis要设计成单线程模式的呢?能够总结以下: 并发
(1)IO多路复用异步
咱们来看一下Redis顶层设计。性能
Redis为何又引入了多线程?做者也逃不过“真香定理”? FD是一个文件描述符,意思是表示当前文件处于可读、可写仍是异常状态。使用 I/O 多路复用机制同时监听多个文件描述符的可读和可写状态。你能够理解为具备了多线程的特色。测试
一旦受到网络请求就会在内存中快速处理,因为绝大多数的操做都是纯内存的,因此处理的速度会很是地快。也就是说在单线程模式下,即便链接的网络处理不少,由于有IO多路复用,依然能够在高速的内存处理中获得忽略。
(2)可维护性高
多线程模型虽然在某些方面表现优异,可是它却引入了程序执行顺序的不肯定性,带来了并发读写的一系列问题。单线程模式下,能够方便地进行调试和测试。
(3)基于内存,单线程状态下效率依然高
多线程可以充分利用CPU的资源,但对于Redis来讲,因为基于内存速度那是至关的高,能达到在一秒内处理10万个用户请求,若是一秒十万还不能知足,那咱们就可使用Redis分片的技术来交给不一样的Redis服务器。这样的作饭避免了在同一个 Redis 服务中引入大量的多线程操做。
并且基于内存,除非是要进行AOF备份,不然基本上不会涉及任何的 I/O 操做。这些数据的读写因为只发生在内存中,因此处理速度是很是快的;用多线程模型处理所有的外部请求可能不是一个好的方案。
如今咱们知道了基本上能够总结成两句话,基于内存并且使用多路复用技术,单线程速度很快,又保证了多线程的特色。由于没有必要使用多线程。
3、为何引入多线程? 刚刚说了一堆使用单线程的好处,如今话锋一转,又要说为何要引入多线程,别不适应。引入多线程说明Redis在有些方面,单线程已经不具备优点了。
由于读写网络的read/write系统调用在Redis执行期间占用了大部分CPU时间,若是把网络读写作成多线程的方式对性能会有很大提高。
Redis 的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程。之因此这么设计是不想 Redis 由于多线程而变得复杂,须要去控制 key、lua、事务,LPUSH/LPOP 等等的并发问题。
Redis 在最新的几个版本中加入了一些能够被其余线程异步处理的删除操做,也就是咱们在上面提到的 UNLINK、FLUSHALL ASYNC 和 FLUSHDB ASYNC,咱们为何会须要这些删除操做,而它们为何须要经过多线程的方式异步处理?
咱们知道Redis可使用del命令删除一个元素,若是这个元素很是大,可能占据了几十兆或者是几百兆,那么在短期内是不能完成的,这样一来就须要多线程的异步支持。
4、总结 Redis 选择使用单线程模型处理客户端的请求主要仍是由于 CPU 不是 Redis 服务器的瓶颈,因此使用多线程模型带来的性能提高并不能抵消它带来的开发成本和维护成本,系统的性能瓶颈也主要在网络 I/O 操做上;而 Redis 引入多线程操做也是出于性能上的考虑,对于一些大键值对的删除操做,经过多线程非阻塞地释放内存空间也能减小对 Redis 主线程阻塞的时间,提升执行的效率。
一句话讲完:以前用单线程是由于基于内存速度快,并且多路复用有多路复用的做用,也就是足够了,如今引入是由于在某些操做要优化,好比删除操做,所以引入了多线程。