为什么 Redis 设计为单线程?6.0 版本为何引入多线程?

Sherwin.Wei Lv7

为什么 Redis 设计为单线程?6.0 版本为何引入多线程?

回答重点

单线程设计原因

  1. Redis 的操作是基于内存的,其大多数操作的性能瓶颈主要不是 CPU 导致的
  2. 使用单线程模型,代码简便的同时也减少了线程上下文切换带来的性能开销
  3. Redis 在单线程的情况下,使用 I/O 多路复用模型就可以提高 Redis 的 I/O 利用率了

6.0 版本引入多线程的原因

  1. 随着数据规模的增长、请求量的增多,Redis 的执行瓶颈主要在于⽹络 I/O。引入多线程处理可以提高网络 I/O处理速度。

扩展知识

我们所说的 Redis 单线程,主要指的是 Redis 网络 I/O 和键值对读写这些操作是由一个线程完成的。(持久化、集群等机制其实是有后台线程执行的)

不过 Redis 并不是一直都单线程的,在 4.0 之后就开始引入了多线程指令,6.0 之后便正式引入了多线程的机制,不过 这里的多线程其只是针对网络请求过程使用多线程,其对于数据读写命令的处理依旧是单线程的

为什么 Redis 前期不使用多线程的方式,等到 6.0 却又引入呢?

主要是因为我们对 Redis 的性能有了更高的要求,因为随着业务愈加复杂,公司需要的 QPS 就越高了,为了提升 QPS ,最直接的做法就是搭建 Redis 的集群,即提高 Redis 的机器数,但是这种做法的资源消耗是巨大的。

而 Redis 单线程执行命令的性能瓶颈在网络 I/O ,虽然它采用了多路复用技术,但 I/O 多路复用模型本质还是同步I/O

Snipaste_2024-06-22_19-13-28.jpg

从上面这个图中我们可以看到,I/O 多路复用在处理网络请求的时候,其调用 select(或是 epoll 等)后,如果来数据了,那么将数据从内核拷贝到用户空间这一步是同步的(关于 I/O 场景,阻塞、同步等名词意义请看扩展知识),即用户线程需要等待数据拷贝完成(在 redis 场景就是无法处理命令了),如果并发量很高的话,这个过程可能会成为瓶颈

综上所示,我们可以发现,多路复用+单线程的设计并不能很好地解决网络 I/O 瓶颈的问题,这个时候就可以考虑利用 CPU 的多核优势,即利用多线程处理网络请求的方式来提高效率,然后对于读写命令, Redis 依旧采用单线程命令。

Redis 引入多线程之后,有没有带来什么线程安全问题呢

没有,因为 Redis 6.0 只有针对网络请求模块采用的是多线程,对于读写命令部分还是采用单线程,所以所谓的线程安全问题就不存在了。

Redis 6.0 的多线程默认是禁⽤的,只使⽤主线程,因为大部分公司并发量实际上还是用不上这个。

如果要开启需要配置 io-threads-do-reads参数为 yes

I/O 模型

五种 I/O 模型

同步、异步、阻塞、非阻塞的I/O的区别?

Comments