【redis】redis单线程VS多线程

Bennie

面试题引入

  • redis 到底是单线程还是多线程?
  • IO 多路复用听说过吗?
  • redis 为什么这么快?

redis 为什么选择单线程

这种说法其实并不严谨,为什么这么说?
Redis 的版本很多 3.x、4.x、6.x,版本不同架构也是不同的,不限定版本问是否单线程也不太严谨。

  • 版本 3.x ,最早版本,也就是大家口口相传的 redis 是单线程;
  • 版本 4.x,严格意义来说也不是单线程,而是负责处理客户端请求的线程是单线程,但是开始加了点多线程的东西,比如异步删除
  • 2020 年 5 月版本的 6.0.x 后及 2022 年出的 7.0 版本后,告别了大家印象中的单线程,用一种全新的多线程来解决问题。
    5.0 版本是直接升级到 6.0 版本,对于这个激进的升级,Redis 之父 antirez 表现得很有信心和兴奋,所以第一时间发文来阐述 6.0 的一些重大功能”Redis 6.0.0 GA is out!”

redis0

redis 单线程究竟有何意义?
redis 是单线程,主要是指 redis 的网络 IO 和键值对读写由一个线程来完成,redis 在处理客户端的请求包括获取(socket 读)、解析、执行、内容返回(socket 写)等都是由一个顺序串行的主线程处理,这就是所谓的单线程,也是 redis 对外提供键值存储服务的主要流程。
redis1

但是 redis 的其他功能,比如持久化 RDB、AOF,异步删除,集群数据同步等等,其实是由额外的线程执行的,redis 命令工作线程是单线程的,但是,整个 redis 来说,是多线程的。

演进:

  1. redis3.x 单线程时代依旧很快的主要原因
  • 基于内存操作:redis 的所有的数据都存在内存中,因此所有的运算都是内存级别的,所有他的性能比较高
  • 数据结构简单:redis 的数据结构式专门设计的,而这些简单的数据结构的查找和操作的时间大部分都是 0(1),因此性能高
  • 多路复用和非阻塞 I/0: redis 使用 I/O 多路复用功能来监听多个 socket 连接客户端,这样就可以使用一个线程连接来处理多个请求,减少线程切换带来的开销,同时也比避免了 I/O 阻塞操作
  • 避免上下文的切换:因为是单线程的模型,因此就避免了不必要的上下文切换和多线程的竞争,这就省去了多线程切换带来的时间和性能上的消耗,而且单线程不会导致死锁的问题的发生
  1. redis4.0 之前一直采用单线程的主要原因
  • 作者的意思是,使用单线程模型是 redis 开发和维护更简单,
  • 即使使用单线程,也可以并发的处理多客户端的请求,主要使用 io 多路复用和非阻塞 IO
  • 对于 redis 系统来说,主要的性能瓶颈是内存或者网络带宽而并非 CPU

redis6/7 的多线程特性和 IO 多路复用

注意对于redis主要的性能瓶颈时内存或者网络IO而并非CPU.

现在我们来说说网络 IO:
在 Redis6/7 中,非常受关注的第一个新特性就是多线程。
这是因为,Redis 一直被大家熟知的就是它的单线程架构,虽然有些命令操作可以用后台线程或子进程执行(比如数据删除、快照生成、AOF 重写)。但是,从网络 IO 处理到实际的读写命令处理,都是由单个线程完成的。
随着网络硬件的性能提升,Redis 的性能瓶颈有时会出现在网络 IO 的处理上,也就是说,单个主线程处理网络请求的速度跟不上底层网络硬件的速度,
为了应对这个问题:
采用多个 IO 线程来处理网络请求,提高网络请求处理的并行度,Redis6/7 就是采用的这种方法。
但是,Redis 的多 IO 线程只是用来处理网络请求的,对于读写操作命令 Redis 仍然使用单线程来处理。这是因为,Redis 处理请求时,网络处理经常是瓶颈,通过多个 IO 线程并行处理网络操作,可以提升实例的整体处理性能。而继续使用单线程执行命令操作,就不用为了保证 Lua 脚本、事务的原子性,额外开发多线程互斥加锁机制了(不管加锁操作处理),这样一来,Redis 线程模型实现就简单了

主线程和 IO 线程怎么协作完成请求的处理的?
redis2

最后简单的说明
redis 工作线程时单线程的,整个 redis 来说,是多线程的.
I/O 的读和写本身是堵塞的,比如当 socket 中有数据时,Redis 会通过调用先将数据从内核态空间拷贝到用户态空间,再交给 Redis 调用,而这个拷贝的过程就是阻塞的,当数据量越大时拷贝所需要的时间就越多,而这些操作都是基于单线程完成的。
主线程和 IO 线程怎么协作完成请求的处理的?
redis3
从 Redis6 开始,就新增了多线程的功能来提高 I/O 的读写性能,他的主要实现思路是将主线程的 IO 读写任务拆分给一组独立的线程去执行,这样就可以使多个 socket 的读写可以并行化了,采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗),将最耗时的 Socket 的读取、请求解析、写入单独外包出去,剩下的命令执行仍然由主线程串行执行并和内存的数据交互。
redis4
结合上图可知,网络 IO 操作就变成多线程化了,其他核心部分仍然是线程安全的,是个不错的折中办法。

redis7 默认是否开启了多线程?

如果你在实际应用中,发现 redis 实例的 CPU 开销不大单吞吐量却没有上升,可以考虑实现 redis7 的多线程机制,加速网络处理,进而提高吞吐量。
Redis7 将所有数据放在内存中,内存的响应时长大约为 100 纳秒,对于小数据包,Redis 服务器可以处理 8W 到 10W 的 QPS,
这也是 Redis 处理的极限了,对于 80%的公司来说,单线程的 Redis 已经足够使用了。
在 Redis6.0 及 7 后,多线程机制默认是关闭的,如果需要使用多线程功能,需要在 redis.conf 中完成两个设置

  1. 设置 io-thread-do-reads 配置项为 yes,表示启动多线程。
  2. 设置 io-threads,设置线程个数。关于线程数的设置,官方的建议是如果为 4 核的 CPU,建议线程数设置为 2 或 3,如果为 8 核 CPU 建议线程数设置为 6,线程数一定要小于机器核数,线程数并不是越大越好。
  • 标题: 【redis】redis单线程VS多线程
  • 作者: Bennie
  • 创建于 : 2024-02-26 19:45:30
  • 更新于 : 2024-02-26 15:48:25
  • 链接: https://liubin.ink/2024/02/26/redis/【redis】redis单线程VS多线程/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
 评论