提起 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