本文是大数据系列第 48 篇,深入解析 Redis 的通信协议 RESP 和基于 Reactor 模式的事件驱动架构。
为什么要了解 Redis 底层通信?
Redis 以”单线程处理命令却能支撑高并发”著称。这背后依赖两个核心设计:RESP 协议保障高效、轻量的网络通信;Reactor 事件驱动模型实现单线程多路复用 I/O。理解这两者,是深度调优 Redis 和排查连接问题的基础。
RESP 协议(Redis Serialization Protocol)
RESP 是 Redis 自定义的轻量序列化协议,设计目标是简单、高效、可读性好、对二进制数据安全。
五种数据类型
| 类型 | 首字节 | 示例 |
|---|---|---|
| 简单字符串(Simple String) | + | +OK\r\n |
| 错误(Error) | - | -ERR unknown command\r\n |
| 整数(Integer) | : | :1000\r\n |
| 批量字符串(Bulk String) | $ | $6\r\nfoobar\r\n |
| 数组(Array) | * | *2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n |
所有数据以 \r\n(CRLF)结尾,格式固定、解析成本极低。
请求与响应示例
客户端执行 SET mykey myvalue,实际发送的字节流:
*3\r\n
$3\r\nSET\r\n
$5\r\nmykey\r\n
$7\r\nmyvalue\r\n
服务端返回:
+OK\r\n
RESP 的设计使得协议解析可以用几十行代码实现,极大降低了客户端库的开发难度。
通信模式
串行模式(Serial)
客户端发送请求,等待响应,再发下一个请求。每次 RTT(Round-Trip Time)都是纯网络开销,吞吐量受限于延迟。
Pipeline 模式
Pipeline 允许客户端批量发送多个命令,不等待每条命令的响应,服务端处理完毕后统一返回结果,将多次 RTT 压缩为一次。
Jedis Pipeline 示例:
Jedis redis = new Jedis("host", 6379);
Pipeline pipe = redis.pipelined();
for (int i = 0; i < 50000; i++) {
pipe.set("key_" + i, String.valueOf(i));
}
pipe.sync(); // 统一发送并等待所有响应
Pipeline 在批量写入场景下性能可提升 10 倍以上,但注意单个 Pipeline 中命令数量不宜过多(建议 100-1000 条),避免一次性传输数据量过大。
Reactor 事件驱动模型
Redis 采用单线程 + I/O 多路复用架构,通过 Reactor 模式实现对大量并发连接的高效处理。
I/O 多路复用机制对比
| 机制 | 平台 | 最大 FD | 扫描方式 | 性能 |
|---|---|---|---|---|
select | 跨平台 | 1024(FD_SETSIZE) | 线性扫描 | 低 |
poll | 跨平台 | 无限制 | 线性扫描 | 中 |
epoll | Linux | ~100,000 | 事件通知 | 高 |
kqueue | BSD/macOS | ~100,000 | 事件通知 | 高 |
Redis 在 Linux 下使用 epoll,只在有事件就绪时唤醒,不做无效轮询,是支撑高并发的关键。
核心数据结构
aeEventLoop(事件循环)
├── FileEvent[] ← 监听 socket 的读写事件
└── TimeEvent[] ← 定时任务(如 serverCron)
请求处理完整流程
1. 客户端 connect → epoll 检测到连接事件
2. acceptTcpHandler 建立连接,注册读事件
3. 客户端发送命令 → 触发可读事件
4. readQueryFromClient 读取数据,按 RESP 协议解析命令
5. 命令处理器执行业务逻辑,写入响应缓冲区
6. 注册写事件 → sendReplyToClient 将结果发送回客户端
整个流程在单线程事件循环中串行执行,不存在线程切换和锁竞争,这正是 Redis 命令处理高性能的根源。
单线程架构的优缺点
优点:
- 无上下文切换开销,无锁竞争
- 数据结构实现无需考虑并发安全
- 代码逻辑简单,易于维护和调试
局限性:
- 受限于单核 CPU,无法利用多核
- 耗时命令(如
KEYS *、LRANGE大范围)会阻塞整个事件循环 - 大 key 的读写和删除操作会导致其他请求延迟上升
Redis 6.0 多线程 I/O
Redis 6.0 引入了多线程网络 I/O(读取请求、发送响应由多线程处理),但命令执行仍是单线程。这解决了网络 I/O 成为瓶颈的问题,同时保持了命令执行的串行性,无需引入复杂的并发控制。
# redis.conf 开启多线程 I/O(默认关闭)
io-threads 4
io-threads-do-reads yes
总结
Redis 的高性能来自三个层面的精巧设计:
- RESP 协议:轻量高效,解析简单,支持 Pipeline 批处理
- epoll 多路复用:单线程监听数万连接,只处理就绪事件
- Reactor 事件循环:将网络 I/O 和命令执行统一在单线程中,避免并发开销