为什么单线程的 Redis 也能如此高效?多线程又带来了什么?

数据库 Redis
Redis 在 6.0 版本引入多线程支持,主要是为了应对现代硬件架构的变化和日益复杂的业务需求,同时解决单线程模型在高并发场景下的性能瓶颈。

提起 Redis,一个印象就是速度很快的开源内存数据库,而且还是单线程的。

但印象有时会骗人:

  • redis 6.0 后采用了多线程
  • redis 7.4 之后修改了开源协议

单线程为什么速度还很快?

Redis 的客户端调用服务器经过三个过程:发送命令、执行命令和返回结果。在命令执行阶段,由于 Redis 处理命令是单线程的,在服务器上到达的所有命令不会立即被执行。所有命令都进入队列并按顺序执行。多个客户端发送的命令执行顺序不确定。但是可以确定的是两个命令不会同时执行,避免了并发问题。这是 Redis 的基本单线程模型。

Redis 完全基于内存,数据存储在内存中。大多数请求都是纯内存操作,所以速度非常快。与传统的磁盘文件数据存储相比,Redis 避免了通过磁盘 I/O 读取数据到内存的开销。

使用单线程可以节省很多上下文切换和 CPU 消耗的时间,没有竞争条件,不需要考虑各种锁定问题,并且不会因为死锁而导致性能开销。此外,它还允许使用各种「线程不安全」的命令,例如 Lpush。

需要注意的是,当我们强调单线程时,我们指的是使用一个线程来处理网络 I/O 和键值对读写。换句话说,一个线程处理所有网络请求。但 Redis 的其他功能,如持久性、异步删除和集群数据同步,实际上是由额外的线程执行。也就是说即便是在 6.0 之前的版本,也不是绝对的单线程。

Redis 虽然是纯内存操作,但仍然会涉及到网络 I/O 和磁盘 I/O(持久化操作),Redis 使用非阻塞 I/O 和 I/O 多路复用技术(如 select、poll、epoll)来处理大量的并发连接。这意味着一个线程可以同时监听多个客户端的连接请求,并在有数据可读或可写时进行处理。这种方式避免了线程在等待 I/O 操作完成时的阻塞,从而提高了系统的并发处理能力。

  • 非阻塞 I/O:允许程序在 I/O 操作未完成时继续执行其他任务。
  • I/O 多路复用:通过单线程监控多个 I/O 操作,提升高并发下的效率。

官方对单线程的解释是:因为 CPU 不是 Redis 的瓶颈,最有可能的是机器内存或网络带宽。由于单线程易于实现,并且 CPU 不会成为瓶颈,采用单线程解决方案是有意义的。

为什么 6.0 又采用了多线程?

Redis 在 6.0 版本引入多线程支持,主要是为了应对现代硬件架构的变化和日益复杂的业务需求,同时解决单线程模型在高并发场景下的性能瓶颈。

1.提升网络 I/O 性能

Redis 的主要性能瓶颈在于网络 I/O,尤其是在高并发场景下,单线程模型难以高效处理大量的网络请求。虽然 Redis 使用了 I/O 多路复用技术(如 epoll)来优化网络处理,但随着硬件性能的提升,单线程的网络 I/O 处理能力逐渐成为瓶颈。

通过引入多线程,Redis 可以将网络 I/O 操作(如读取客户端请求和写回响应)分配到多个线程并行处理,从而显著提升网络吞吐量和降低延迟。

2.利用多核 CPU

现在的服务器普遍配备多核 CPU,而 Redis 的单线程模型只能利用一个 CPU 核心,无法充分发挥硬件的性能潜力。通过引入多线程,Redis 可以将网络 I/O 任务分配到多个线程,充分利用多核 CPU 的计算能力,从而提高整体性能。

3.保持核心逻辑的简单性和一致性

Redis 6.0 的多线程模型采用了混合设计:网络 I/O 操作由多个线程并行处理,而命令执行仍然由单线程顺序执行。这种设计既保留了单线程模型的简单性和一致性优势(如避免线程安全问题、保证命令的原子性),又通过多线程提升了网络 I/O 的性能。和之前的版本相比,只是将更多的事情由多线程来处理。

4.优化资源利用

在高负载场景下,单线程模型可能导致 CPU 和内存资源的浪费。通过引入多线程,Redis 可以更高效地利用系统资源,减少请求的等待时间,从而提升整体性能。

5.如何在 Redis 6.0 中启用多线程?

默认情况下,Redis 的多线程是禁用的,如果要启用多线程功能,需要修改 Redis 的配置文件,涉及到两个配置项:

io-threads:

  • 该选项用于设置 I/O 线程的数量。
  • 默认值为 1,即不启用多线程。
  • 如果设置为大于 1 的值,Redis 会启用多线程来处理网络 I/O。
  • 建议将 io-threads 设置为小于 CPU 核心数的值,通常为 CPU 核心数的 1/2 到 2/3。

io-threads-do-reads:

  • 该选项用于控制是否启用多线程处理读操作。
  • 默认值为 no,即不启用多线程读操作。
  • 如果需要启用多线程读操作,可以将其设置为 yes。

配置示例如下:

# 启用多线程,设置 I/O 线程数为 4
io-threads 4

# 启用多线程处理读操作
io-threads-do-reads yes

修改了开源协议,有什么替代方案

Redis 从 7.4 版本开始修改了开源协议,从 BSD 变更为 RSALv2 和 SSPLv1 双重许可,这意味着 Redis 在 OSI(开放源代码促进会)定义下不再被视为严格的开源软件。

这一变更对云服务商和开发者产生了较大影响,尤其是那些依赖 Redis 提供商业服务的厂商。

可以使用 Valkey 进行代替,Valkey 由 Linux 基金会支持,采用 BSD 许可证,确保了项目的开源性质。自 2024 年 3 月由 Redis 项目的贡献者和 Linux 基金会联合发起以来,Valkey 已经得到了包括亚马逊云科技在内的 40 多家公司的支持和贡献。

我现在在研究的 RAGFlow 使用的就是 Valkey 。下面是 RAGFlow 中部署 Valkey 的 docker-compose 部分代码:

redis:
  image: valkey/valkey:8
  container_name: ragflow-redis
  command: redis-server --requirepass ${REDIS_PASSWORD} --maxmemory 128mb --maxmemory-policy allkeys-lru
  env_file: .env
  ports:
    - ${REDIS_PORT}:6379
  volumes:
    - redis_data:/data
  networks:
    - ragflow
  restart: on-failure
责任编辑:姜华 来源: 不止dotNET
相关推荐

2023-10-15 12:23:10

单线程Redis

2023-08-17 14:12:17

2020-10-30 16:20:38

Redis单线程高并发

2020-06-11 09:35:39

Redis单线程Java

2023-03-21 08:02:36

Redis6.0IO多线程

2020-11-17 10:20:53

Redis多线程单线程

2024-02-26 00:00:00

JavaScript单线程高效

2021-03-03 08:01:58

Redis多线程程序

2019-06-17 14:20:51

Redis数据库Java

2019-05-07 09:44:45

Redis高并发模型

2021-03-15 09:40:59

Redis单线程效率

2019-05-06 11:12:18

Redis高并发单线程

2019-02-18 08:10:53

Redis单线程Rehash

2020-11-09 09:33:37

多线程

2024-09-27 11:51:33

Redis多线程单线程

2020-10-16 16:00:50

Redis单线程数据库

2021-06-11 11:28:22

多线程fork单线程

2021-12-28 09:50:18

Redis单线程高并发

2023-12-01 08:18:24

Redis网络

2020-09-23 13:37:25

Redis6.0
点赞
收藏

51CTO技术栈公众号