Redis-为什么使用多线程?

一位同事面试腾讯时,面试问了这么一个问题:Redis为什么又采用多线程了,不是一直单线程的?,听到这个问题第一时间脑子有点懵,他一直没有注意这个问题,导致回答不上来。

接下来我们就以这个问题展开讲解,Redis是目前使用非常广泛的一个内存数据库,在各个场景中都有着非常丰富的应用,Redis 6.0 之后的版本抛弃了单线程模型,原本使用单线程运行的 Redis 也开始选择性使用多线程模型,就算Redis的作者多牛,也逃不过“真香定律”!

Redis-为什么使用多线程?_第1张图片

对于这个问题可以拆分成以下两个问题进行解答:

  • 为什么 Redis 一开始选择单线程模型?
  • Redis不是已经采用了多路复用技术吗?不是号称很高的性能了吗?为什么在 Redis 6.0 之后加入了多线程?

为什么 Redis 一开始选择单线程模型?

Redis 作为一个成熟的分布式缓存框架,它由很多个模块组成,如网络请求模块、索引模块、存储模块、高可用集群支撑模块、数据操作模块等。

在很多人的理解当中认为 Redis 是单线程的,认为 Redis 中所有模块的操作都是单线程的,其实不对。我们说的 Redis 单线程指的是网络IO和键值对读写是由一个线程完成的,也就是说,Redis中只有网络请求模块和数据操作模块是单线程的。而其他的如持久化存储模块、集群支撑模块等是多线程的。

早在 Redis4.0版本的时候就已经对部分命令做了多线程化。

那么,为什么网络操作模块和数据存储模块最初并没有使用多线程呢?因为:“没必要!”

因为Redis是一个基于内存的数据库,还要处理大量的外部的网络请求,这就不可避免的要进行多次IO。好在Redis使用了很多优秀的机制来保证了它的高效率。那么为什么Redis要设计成单线程模式的呢?

  • IO多路复用
  • Redis在网络I/O模型上采用了多路复用技术,来减少网络瓶颈带来的影响。很多场景中使用单线程模型并不意味着程序不能并发的处理任务。Redis 虽然使用单线程模型处理用户的请求,但是它却使用 I/O 多路复用技术“并行”处理来自客户端的多个连接,同时等待多个连接发送的请求。使用 I/O 多路复用技术能极大地减少系统的开销,系统不再需要为每个连接创建专门的监听线程,避免了由于大量的线程创建带来的巨大性能开销。
  • 可维护性
  • 多线程模型虽然在某些方面表现优异,但是它却引入了程序执行顺序的不确定性,带来了并发读写的一系列问题。单线程模式下,可以方便地进行调试和测试。
  • 基于内存
  • 多线程能够充分利用 CPU 的资源,但对于Redis来说,由于基于内存速度那是相当的高,能达到在一秒内处理10万个用户请求,如果一秒十万还不能满足,那我们就可以使用 Redis 分片的技术来交给不同的 Redis 服务器。这样的做饭避免了在同一个 Redis 服务中引入大量的多线程操作。 而且基于内存,除非是要进行 AOF 备份,否则基本上不会涉及任何的 I/O 操作。这些数据的读写由于只发生在内存中,所以处理速度是非常快的;用多线程模型处理全部的外部请求可能不是一个好的方案
  • 线程上下文切换问题
  • 多线程场景下会发生线程上下文切换。线程是由 CPU 调度的,CPU 的一个核在一个时间片内只能同时执行一个线程,在 CPU 由线程A切换到线程B的过程中会发生一系列的操作,主要过程包括保存线程A的执行现场,然后载入线程B的执行现场,这个过程就是“线程上下文切换”。其中涉及线程相关指令的保存和恢复。 频繁的线程上下文切换可能会导致性能急剧下降,这会导致我们不仅没有提升处理请求的速度,反而降低了性能。

基于上面几点可以总结为:基于内存而且使用多路复用技术,单线程速度很快,又保证了多线程的特点。因为没有必要使用多线程。

首先,我们可以肯定的说,Redis 不需要提升 CPU 利用率,因为 Redis 的操作基本都是基于内存的,CPU 资源根本就不是 Redis 的性能瓶颈。

虽然说采用多线程可以帮助我们提升 CPU 和 I/O 的利用率,但是多线程带来的并发问题也给这些语言和框架带来了更多的复杂性。而且,多线程模型中,多个线程的互相切换也会带来一定的性能开销。所以,在提升 I/O 利用率这个方面上,Redis 并没有采用多线程技术,而是选择了多路复用 I/O 技术。

所以,通过多线程技术来提升 Redis 的 CPU 利用率这一点是完全没必要的。

Redis 并不是完全单线程的,只是有关键的网络 IO 和键值对读写是由一个线程完成的,这点我们需要记住一下

为什么Redis 6.0 引入多线程?

2020年5月份,Redis正式推出了6.0版本,这个版本中有很多重要的新特性,其中多线程特性引起了广泛关注。

大家会有一个疑问:
上面不是说 Redis 号称单线程也有很高的性能么?

不是说多路复用技术已经大大的提升了 IO 利用率了么,为啥还需要多线程?

Redis 将所有数据放在内存中,内存的响应时长大约为100纳秒,对于小数据包,Redis 服务器可以处理80,000到100,000 QPS,这也是Redis处理的极限了,对于80%的公司来说,单线程的Redis已经足够使用了。

但随着越来越复杂的业务场景,有些公司动不动就上亿的交易量,因此需要更大的QPS。

常见的解决方案是在分布式架构中对数据进行分区并采用多个服务器,但该方案有非常大的缺点,例如要管理的Redis服务器太多,维护代价大;某些适用于单个 Redis 服务器的命令不适用于数据分区;数据分区无法解决热点读/写问题;数据偏斜,重新分配和放大/缩小变得更加复杂等等。

虽然现在很多服务器都是多个 CPU 核的,但是对于Redis来说,因为使用了单线程,在一次数据操作的过程中,有大量的 CPU 时间片是耗费在了网络 IO 的同步处理上的,并没有充分的发挥出多核的优势。

如果能采用多线程,使得网络处理的请求并发进行,就可以大大的提升性能。多线程除了可以减少由于网络 I/O 等待造成的影响,还可以充分利用 CPU 的多核优势。

Redis6.0引入的多线程部分,实际上只是用来处理网络数据的读写和协议解析,执行命令仍然是单一工作线程。
Redis-为什么使用多线程?_第2张图片

从上图我们可以看到 Redis 在处理网络数据时,调用epoll的过程是阻塞的,也就是说这个过程会阻塞线程,如果并发量很高,达到几万的QPS,此处可能会成为瓶颈。一般我们遇到此类网络 IO 瓶颈的问题,可以增加线程数来解决。开启多线程除了可以减少由于网络 I/O 等待造成的影响,还可以充分利用 CPU 的多核优势。Redis6.0 也不例外,在此处增加了多线程来处理网络数据,以此来提高 Redis的 吞吐量。当然相关的命令处理还是单线程运行,不存在多线程下并发访问带来的种种问题。

你可能感兴趣的:(redis,redis,缓存,数据库)