Redis是目前广为人知的一个内存数据库,在各个场景中都有着十分丰盛的利用,前段时间Redis推出了6.0的版本,在新版本中采纳了多线程模型。
因为咱们公司应用的内存数据库是自研的,按理说我对Redis的关注其实并不算多,然而因为Redis用的比拟宽泛,所以我须要理解一下这样不便我进行面试。
总不能候选人用过Redis,然而我非要问人家阿里的Tair是怎么回事吧。
所以,在Redis 6.0 推出之后,我想去理解下为什么采纳多线程,当初采纳的多线程和以前版本有什么区别?为什么这么晚才应用多线程?
Redis不是曾经采纳了多路复用技术吗?不是号称很高的性能了吗?为啥还要采纳多线程模型呢?
本文就来剖析下这些问题以及背地的思考。
Redis为什么最开始被设计成单线程的?
Redis作为一个成熟的分布式缓存框架,它由很多个模块组成,如网络申请模块、索引模块、存储模块、高可用集群撑持模块、数据操作模块等。
很多人说Redis是单线程的,就认为Redis中所有模块的操作都是单线程的,其实这是不对的。
咱们所说的Redis单线程,指的是"其网络IO和键值对读写是由一个线程实现的",也就是说,Redis中只有网络申请模块和数据操作模块是单线程的。而其余的如长久化存储模块、集群撑持模块等是多线程的。
所以说,Redis中并不是没有多线程模型的,早在Redis 4.0的时候就曾经针对局部命令做了多线程化。
那么,为什么网络操作模块和数据存储模块最后并没有应用多线程呢?
这个问题的答案比较简单!因为:"没必要!"
为什么没必要呢?咱们先来说一下,什么状况下要应用多线程?
多线程实用场景
一个计算机程序在执行的过程中,次要须要进行两种操作别离是读写操作和计算操作。
其中读写操作次要是波及到的就是I/O操作,其中包含网络I/O和磁盘I/O。计算操作次要波及到CPU。
而多线程的目标,就是通过并发的形式来晋升I/O的利用率和CPU的利用率。
那么,Redis需不需要通过多线程的形式来晋升晋升I/O的利用率和CPU的利用率呢?
首先,咱们能够必定的说,Redis不须要晋升CPU利用率,因为Redis的操作根本都是基于内存的,CPU资源基本就不是Redis的性能瓶颈。
所以,通过多线程技术来晋升Redis的CPU利用率这一点是齐全没必要的。
那么,应用多线程技术来晋升Redis的I/O利用率呢?是不是有必要呢?
Redis的确是一个I/O操作密集的框架,他的数据操作过程中,会有大量的网络I/O和磁盘I/O的产生。要想晋升Redis的性能,是肯定要晋升Redis的I/O利用率的,这一点毋庸置疑。
然而,晋升I/O利用率,并不是只有采纳多线程技术这一条路能够走!
多线程的弊病
咱们在很多文章中介绍过一些Java中的多线程技术,如内存模型、锁、CAS等,这些都是Java中提供的一些在多线程状况下保障线程平安的技术。
线程平安:是编程中的术语,指某个函数、函数库在并发环境中被调用时,可能正确地解决多个线程之间的共享变量,使程序性能正确实现。
和Java相似,所有反对多线程的编程语言或者框架,都不得不面对的一个问题,那就是如何解决多线程编程模式带来的共享资源的并发管制问题。
尽管,采纳多线程能够帮忙咱们晋升CPU和I/O的利用率,然而多线程带来的并发问题也给这些语言和框架带来了更多的复杂性。而且,多线程模型中,多个线程的相互切换也会带来肯定的性能开销。
所以,在晋升I/O利用率这个方面上,Redis并没有采纳多线程技术,而是抉择了多路复用 I/O技术。
小结
Redis并没有在网络申请模块和数据操作模块中应用多线程模型,次要是基于以下四个起因:
- 1、Redis 操作基于内存,绝大多数操作的性能瓶颈不在 CPU
- 2、应用单线程模型,可维护性更高,开发,调试和保护的老本更低
- 3、单线程模型,防止了线程间切换带来的性能开销
- 4、在单线程中应用多路复用 I/O技术也能晋升Redis的I/O利用率
还是要记住:Redis并不是齐全单线程的,只是有要害的网络IO和键值对读写是由一个线程实现的。
Redis的多路复用
多路复用这个词,置信很多人都不生疏。我之前的很多文章中也够提到过这个词。
其中在介绍Linux IO模型的时候咱们提到过它、在介绍HTTP/2的原理的时候,咱们也提到过他。
那么,Redis的多路复用技术和咱们之前介绍的又有什么区别呢?
这里先讲讲Linux多路复用技术,就是多个过程的IO能够注册到同一个管道上,这个管道会对立和内核进行交互。当管道中的某一个申请须要的数据筹备好之后,过程再把对应的数据拷贝到用户空间中。

多看一遍下面这张图和下面那句话,前面可能还会用失去。
也就是说,通过一个线程来解决多个IO流。
IO多路复用在Linux下包含了三种,select、poll、epoll,形象来看,他们性能是相似的,但具体细节各有不同。
其实,Redis的IO多路复用程序的所有性能都是通过包装操作系统的IO多路复用函数库来实现的。每个IO多路复用函数库在Redis源码中都有对应的一个独自的文件。

在Redis 中,每当一个套接字筹备好执行连贯应答、写入、读取、敞开等操作时,就会产生一个文件事件。因为一个服务器通常会连贯多个套接字,所以多个文件事件有可能会并发地呈现。

一旦有申请达到,就会交给 Redis 线程解决,这就实现了一个 Redis 线程解决多个 IO 流的成果。
所以,Redis抉择应用多路复用IO技术来晋升I/O利用率。
而之所以Redis可能有这么高的性能,不仅仅和采纳多路复用技术和单线程无关,此外还有以下几个起因:
- 1、齐全基于内存,绝大部分申请是纯正的内存操作,十分疾速。
- 2、数据结构简略,对数据操作也简略,如哈希表、跳表都有很高的性能。
- 3、采纳单线程,防止了不必要的上下文切换和竞争条件,也不存在多过程或者多线程导致的切换而耗费 CPU
- 4、应用多路I/O复用模型
为什么Redis 6.0 引入多线程
2020年5月份,Redis正式推出了6.0版本,这个版本中有很多重要的新个性,其中多线程个性引起了宽泛关注。
然而,须要揭示大家的是,Redis 6.0中的多线程,也只是针对解决网络申请过程采纳了多线程,而数据的读写命令,依然是单线程解决的。
然而,不晓得会不会有人有这样的疑难:
Redis不是号称单线程也有很高的性能么?
不是说多路复用技术曾经大大的晋升了IO利用率了么,为啥还须要多线程?
次要是因为咱们对Redis有着更高的要求。
依据测算,Redis 将所有数据放在内存中,内存的响应时长大概为 100 纳秒,对于小数据包,Redis 服务器能够解决 80,000 到 100,000 QPS,这么高的对于 80% 的公司来说,单线程的 Redis 曾经足够应用了。
但随着越来越简单的业务场景,有些公司动不动就上亿的交易量,因而须要更大的 QPS。
为了晋升QPS,很多公司的做法是部署Redis集群,并且尽可能晋升Redis机器数。然而这种做法的资源耗费是微小的。
而通过剖析,限度Redis的性能的次要瓶颈呈现在网络IO的解决上,尽管之前采纳了多路复用技术。然而咱们后面也提到过,多路复用的IO模型实质上依然是同步阻塞型IO模型。
上面是多路复用IO中select函数的处理过程:

从上图咱们能够看到,在多路复用的IO模型中,在解决网络申请时,调用 select (其余函数同理)的过程是阻塞的,也就是说这个过程会阻塞线程,如果并发量很高,此处可能会成为瓶颈。
尽管当初很多服务器都是多个CPU核的,然而对于Redis来说,因为应用了单线程,在一次数据操作的过程中,有大量的CPU工夫片是消耗在了网络IO的同步解决上的,并没有充沛的施展出多核的劣势。
如果能采纳多线程,使得网络解决的申请并发进行,就能够大大的晋升性能。多线程除了能够缩小因为网络 I/O 期待造成的影响,还能够充分利用 CPU 的多核优势。
所以,Redis 6.0采纳多个IO线程来解决网络申请,网络申请的解析能够由其余线程实现,而后把解析后的申请交由主线程进行理论的内存读写。晋升网络申请解决的并行度,进而晋升整体性能。
然而,Redis 的多 IO 线程只是用来解决网络申请的,对于读写命令,Redis 依然应用单线程来解决。
那么,在引入多线程之后,如何解决并发带来的线程平安问题呢?
这就是为什么咱们后面屡次提到的"Redis 6.0的多线程只用来解决网络申请,而数据的读写还是单线程"的起因。
Redis 6.0 只有在网络申请的接管和解析,以及申请后的数据通过网络返回给时,应用了多线程。而数据读写操作还是由单线程来实现的,所以,这样就不会呈现并发问题了。
参考资料:
https://www.cnblogs.com/Zzbj/...
https://xie.infoq.cn/article/...
https://jishuin.proginn.com/p...
《极客工夫:Redis核心技术与实战》