还不懂Redis是单线程还是多线程的?这份Redis实战该学起来了!

本文详细解析了Redis的单线程工作原理及其在4.0版本后引入的多线程特性,包括LazyFree机制、I/O多路复用及Redis 6.0的多线程实现。

前言

 

Redis,在工作中我们经常使用到,这也决定了它是面试的常客。我们总说Redis是单线程的,其实这也没错也错了,因为Redis不仅仅是单线程,从4.0开始,Redis就引进了多线程的概念。

单线程

我们常说的Redis单线程其实是指事件处理上的。在了解事件处理上,先了解下redis的工作机制。
Redis服务器是一个事件驱动程序,服务器需要处理以下两类事件:

  • 文件事件:Redis服务器通过套接字与客户端(或者其他Redis服务器)进行连接,而文件事件就是服务器对套接字操作的抽象;服务器与客户端的通信会产生相应的文件事件,而服务器则通过监听并处理这些事件来完成一系列网络通信操作,比如连接accept,read,write,close。
  • 时间事件:Redis服务器中的一些操作需要在给定的时间点执行,而时间事件就是服务器对这类定时操作的抽象,比如过期键清理

Redis将文件事件和时间事件进行抽象,时间轮训器会监听I/O事件表,一旦有文件事件就绪,Redis就会优先处理文件事件,接着处理时间事件。
在所有事件处理上,Redis都是以单线程形式处理,所以说Redis是单线程的。
同时,Redis基于Reactor模式开发了自己的I/O事件处理器,也就是文件事件处理器,Redis在I/O事件处理上,采用了I/O多路复用技术,同时监听多个套接字,并为套接字关联不同的事件处理函数,通过一个线程实现了多客户端并发处理。
但是,实际上Redis也并不是单线程的,比如生成RDB文件,就会fork一个子进程来实现。
在4.0之前,Redis在处理客户端命名时都是以单线程形式运行的,期间不响应其他客户端请求;但是如果发生一条耗时很长的命令,如:删除一个大key,导致Redis服务器卡住了几秒,对负载高的缓存系统是一种灾难,因此,在4.0版本引入了Lazy Free机制,将慢操作异步化了。

I/O多路复用
- I/O :网络 I/O 
- 多路:多个客户端连接(连接就是套接字描述符,即 socket 或者 channel) 
- 复用:复用一个或几个线程。就是说一个或一组线程处理多个 TCP 连接,使用单进程就能够实现同时处理多个客户端的连接 

就是说`一个服务端进程可以同时处理多个套接字描述符`

Lazy Free机制

将大键的删除操作异步化,采用非阻塞删除(对应命令UNLINK),大键的空间回收交由单独线程实现,主线程只做关系解除,可以快速返回,继续处理其他事件,避免服务器长时间阻塞。

Redis6.0后

Redis在4.0版本引入了Lazy Free,自此Redis有了一个Lazy Free线程专门用于大键的回收,但是大家都知道,Redis的性能瓶颈并不在CPU上,而是在内存和网络上。所以6.0发布的多线程并未将事件处理改成多线程,而是在I/O上。
如果把事件处理改成多线程,不但会导致锁竞争,而且会有频繁的上下文切换,即使用分段锁来减少竞争,对Redis内核也会有较大改动,性能也不一定有明显提升。
但是,6.0版本的多线程并非彻底的多线程,I/O线程只能同时执行读或者同时执行写操作,期间事件处理线程一直处于等待状态,并非流水线模型,有很多轮训等待开销。
在 Redis6.0 中,多线程机制默认是关闭的,需要在 redis.conf 中完成以下两个设置才能启用多线程。


设置 io-thread-do-reads 配置项为 yes,表示启用多线程。
io-threads-do-reads yes

设置线程个数

io-threads 6

⼀般来说,线程个数要小于 Redis 实例所在机器的 CPU 核数,例如,对于⼀个 4 核的机器来说,Redis 官⽅建议配置 2 个 或 3个IO 线程。
线程数并不是越大越好,官方认为超过了8个基本就没什么意义了。

总结

Redis的单线程,主要是指事件处理上,但是Redis的其他功能,如持久化、异步删除这些都是由额外的线程执行的。在Redis6.0版本的多线程并非彻底的多线程,I/O线程只能同时执行读或者同时执行写操作。在开启多线程后,并不会存在线程并发的安全问题,因为Redis的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程顺序执行。

这里分享一份八年Java开发大牛呕心沥血整理出来的 Redis实战,先简单的看一下大概内容

第一部分:入门

第1章 初识Redis

第2章 使用Redis构建Web

第二部分:核心概念

第3章 Redis命令

第4章 数据安全与性能保障

第5章 使用Redis构建支持程序

第6章 使用Redis构建应用程序组件

第7章 基于搜索的应用程序

第8章 构建简单的社交网站

第三部分:进阶内容

第9章 降低内存占用

第10章 扩展Redis

第11章 Redis的Lua脚本编程

### Redis 线程模型概述 Redis线程模型经历了同的发展阶段,早期版本(v4.0 之前)主要采用单线程模型,而自 Redis v6.0 开始引入了多线程支持。以下是两种模型的区别及其特点。 #### 单线程模型 Redis 在 v6.0 之前的版本中采用了经典的单线程架构[^3]。这种模型的核心在于通过个事件循环(event loop),利用 I/O 多路复用技术(如 `select`、`epoll` 或 `kqueue`)来高效地管理客户端连接并处理请求。具体来说: - **文件事件处理器**:Redis 基于 Reactor 模式实现了文件事件处理器(File Event Handler)。该处理器负责监听多个 socket 连接上的事件,并根据事件类型调用相应的回调函数进行处理[^5]。 - **优点**: - 避免了多线程环境下的锁竞争和上下文切换开销。 - 设计简单,易于调试和优化。 - 对于绝大多数场景而言,CPU 并Redis 性能的主要瓶颈;真正的瓶颈通常是网络 I/O[^3]。 然而,单线程模型也有局限性——当某个命令的执行时间较长时(如复杂计算或大对象操作),可能会阻塞整个事件循环,影响其他客户端的响应速度。 --- #### 多线程模型 随着 Redis 功能的扩展和技术需求的变化,Redis 自 v6.0 起引入了部分多线程的支持[^2]。尽管如此,Redis 的核心仍然是围绕单线程运行,新增的多线程特性主要用于特定场景下提升性能。主要包括以下几个方面: - **I/O 处理中的多线程**:在 Redis v6.0 中,网络读写操作可以通过配置启用多线程模式。此时,主线程仍然负责接收新连接和分发任务给工作线程池,而实际的数据读取/写入由这些工作线程完成[^1]。 - **持久化过程中的多线程**:对于 AOF 日志重写等耗时操作,也可以分配独立的工作线程来减少对主线程的影响[^4]。 需要注意的是,即使启用了多线程功能,默认情况下 Redis 的命令执行依然保持串行化,即每次只有个命令会被解析和执行。这有助于维持 Redis 数据结构的致性和高性能表现。 --- ### 单线程 vs 多线程区别总结表 | 特性 | 单线程模型 | 多线程模型 | |-------------------|----------------------------------|----------------------------------| | 核心逻辑 | 所有操作都在单线程内完成 | 主线程配合辅助线程共同协作 | | 使用范围 | 请求处理、数据存储与检索 | 网络 IO 及某些后台任务 | | 上下文切换成本 | 极低 | 较高 | | 锁机制依赖 | 需要额外加锁 | 存在线程间同步问题 | 综上所述,虽然 Redis 已经具备了定程度上的多线程能力,但从整体上看它依旧保留着以单线程为主导的设计理念[^2]。 ```python import redis # 创建 Redis 客户端实例 r = redis.Redis(host='localhost', port=6379, decode_responses=True) # 测试简单的键值操作 r.set('test_key', 'value') print(r.get('test_key')) ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值