Redis 是一个高性能的键值存储系统,广泛应用于缓存、消息队列、分布式锁等场景。本文将深入探讨 Redis 的架构设计、单线程模型和事件驱动机制,帮助你理解 Redis 高性能背后的核心原理。
一、Redis 架构概览
1.1 整体架构
Redis 采用经典的客户端 - 服务器(Client-Server)架构模型:
graph TB
subgraph Clients
C1[客户端 1]
C2[客户端 2]
C3[客户端 3]
end
subgraph Network
TCP[TCP 连接]
end
subgraph Redis Server
CMD[命令请求]
AE[事件驱动]
DB[(内存数据库)]
PS[持久化]
end
C1 --> TCP
C2 --> TCP
C3 --> TCP
TCP --> CMD
CMD --> AE
AE --> DB
AE --> PS
1.2 核心组件
| 组件 | 说明 |
|---|---|
| 网络模块 | 处理 TCP 连接、协议解析 |
| 命令处理器 | 解析并执行客户端命令 |
| 事件驱动 | 基于 Reactor 模式的事件循环 |
| 内存管理 | 对象分配、内存淘汰 |
| 持久化模块 | RDB 快照、AOF 日志 |
二、单线程模型
2.1 为什么选择单线程?
Redis 的核心网络 IO 和键值对读写采用单线程模型,这是 Redis 高性能的关键设计之一:
graph LR
A[客户端请求] --> B[命令队列]
B --> C[单线程处理]
C --> D[执行命令]
D --> E[返回结果]
style C fill:#f9f,stroke:#333,stroke-width:2px
优势:
- 无锁竞争 - 避免多线程的锁开销和上下文切换
- 实现简单 - 代码逻辑清晰,易于维护
- 原子性保证 - 单个命令天然原子执行
- 调试方便 - 单线程更容易排查问题
劣势:
- CPU 多核利用不足 - 单个 Redis 实例无法充分利用多核
- 大 Key 阻塞 - 单个慢命令会阻塞所有请求
2.2 单线程性能分析
Redis 单线程能达到 10 万 + QPS 的原因:
| 因素 | 说明 |
|---|---|
| 纯内存操作 | 数据都在内存中,访问速度极快 |
| 高效数据结构 | SDS、跳表、压缩列表等优化设计 |
| IO 多路复用 | epoll/kqueue 实现高并发网络 IO |
| 协议简单 | RESP 协议解析高效 |
2.3 多线程 IO(Redis 6.0+)
Redis 6.0 引入了多线程 IO,但核心命令执行仍是单线程:
sequenceDiagram
participant C as 客户端
participant M as 主线程
participant W as IO 工作线程
C->>M: 建立连接
M->>W: 分配读任务
W->>W: 读取数据
W->>M: 数据就绪
M->>M: 执行命令
M->>W: 分配写任务
W->>W: 返回数据
W->>C: 发送响应
注意:多线程仅用于网络 IO 的读写,命令执行仍然是单线程,保证了数据一致性。
三、事件驱动机制
3.1 Reactor 模式
Redis 基于 Reactor 模式实现事件驱动:
stateDiagram-v2
[*] --> 文件事件
文件事件 --> AE: 连接请求
文件事件 --> AE: 读请求
文件事件 --> AE: 写请求
AE --> 命令处理: 执行命令
AE --> 网络回复: 返回结果
时间事件 --> AE: 定时任务
时间事件 --> 持久化:RDB/AOF
3.2 文件事件
Redis 使用 IO 多路复用技术(epoll/kqueue)监听多个 socket 连接:
// 简化的事件循环伪代码
while (!stop) {
// 1. 等待事件发生
aeApiPoll(eventLoop, timeout);
// 2. 处理已发生的事件
for (each fired event) {
if (readable) {
acceptConnection(); // 接受连接
readCommand(); // 读取命令
}
if (writable) {
writeResponse(); // 写入响应
}
}
// 3. 执行时间事件
processTimeEvents();
}
3.3 时间事件
Redis 的时间事件用于处理定时任务:
| 任务 | 说明 |
|---|---|
| serverCron | 每秒执行,更新统计信息、清理过期 key |
| RDB 定时保存 | 根据配置触发快照 |
| AOF 重写 | 定期压缩 AOF 文件 |
| 超时检测 | 客户端连接超时、集群节点超时 |
四、内存数据库设计
4.1 全局数据结构
Redis 服务器状态的全局结构:
struct redisServer {
// 数据库数组
redisDb *db;
int dbnum; // 数据库数量
// 网络
aeEventLoop *el; // 事件循环
list *clients; // 客户端列表
// 持久化
int rdb_save_time; // RDB 保存状态
int aof_state; // AOF 状态
// 统计
long long stat_commands_processed;
long long stat_net_input_bytes;
long long stat_net_output_bytes;
};
4.2 数据库结构
每个 Redis 数据库包含:
graph TB
subgraph RedisDB
D1[字典 - 键空间]
D2[字典 - 过期时间]
L1[阻塞列表]
N1[键空间通知]
end
D1 --> K1[Key1: Value1]
D1 --> K2[Key2: Value2]
D2 --> E1[Key1: Expire]
核心组件:
- 键空间字典:存储所有键值对
- 过期字典:记录 key 的过期时间
- 阻塞列表:BLPOP 等阻塞命令
- 键空间通知:发布订阅机制
4.3 过期策略
Redis 采用惰性删除 + 定期删除相结合的策略:
flowchart TD
A[访问 Key] --> B{是否过期?}
B -->|是 | C[删除 Key]
B -->|否 | D[返回 Value]
E[定期任务] --> F[随机采样 N 个 Key]
F --> G{有过期 Key?}
G -->|是 | H[删除过期 Key]
G -->|否 | I[结束]
H --> J{删除比例 > 25%?}
J -->|是 | F
J -->|否 | I
五、网络协议
5.1 RESP 协议
Redis 使用 RESP(Redis Serialization Protocol)协议:
| 类型 | 格式 | 示例 |
|---|---|---|
| 简单字符串 | + | +OK\r\n |
| 错误 | - | -ERR unknown\r\n |
| 整数 | : | :1000\r\n |
| 批量字符串 | $ | $5\r\nhello\r\n |
| 数组 | * | *2\r\n$3\r\nGET\r\n$3\r\nkey\r\n |
5.2 请求 - 响应流程
sequenceDiagram
participant Client
participant Redis
Client->>Redis: *2\r\n$3\r\nGET\r\n$3\r\nkey\r\n
Redis->>Redis: 解析命令
Redis->>Redis: 查找 key
Redis-->>Client: $5\r\nvalue\r\n
六、性能优化要点
6.1 避免阻塞命令
| 危险命令 | 风险 | 替代方案 |
|---|---|---|
KEYS * | 遍历所有 key | 使用 SCAN |
HGETALL | 大 Hash 阻塞 | 使用 HSCAN |
SMEMBERS | 大 Set 阻塞 | 使用 SSCAN |
DEL big-key | 删除大 key 阻塞 | 使用 UNLINK |
6.2 合理设置内存淘汰策略
# redis.conf 配置示例
maxmemory 2gb
maxmemory-policy allkeys-lru # 推荐生产环境使用
常用淘汰策略:
| 策略 | 说明 | 适用场景 |
|---|---|---|
noeviction | 不淘汰,写操作返回错误 | 不允许数据丢失 |
allkeys-lru | 淘汰最近最少使用的 key | 缓存场景 |
volatile-lru | 淘汰有过期时间的 key | 部分数据需要持久 |
allkeys-random | 随机淘汰 | 访问均匀分布 |
6.3 Pipeline 批量操作
# ❌ 低效:N 次网络往返
for i in range(1000):
redis.set(f"key:{i}", i)
# ✅ 高效:1 次网络往返
pipe = redis.pipeline()
for i in range(1000):
pipe.set(f"key:{i}", i)
pipe.execute()
七、总结
核心要点回顾
- 单线程模型 - 避免锁竞争,命令原子执行
- 事件驱动 - Reactor 模式实现高并发
- 内存操作 - 高效数据结构保证性能
- IO 多路复用 - epoll/kqueue 监听多连接
- 多线程 IO - Redis 6.0+ 提升网络吞吐
架构设计启示
| 设计原则 | Redis 实践 |
|---|---|
| 简单即高效 | 单线程减少复杂度 |
| 空间换时间 | 内存存储 + 丰富数据结构 |
| 异步非阻塞 | 事件驱动 + IO 多路复用 |
| 渐进式优化 | 惰性删除 + 定期删除 |
参考资料
- Redis 官方文档 - Architecture
- Redis 源码解析
- 《Redis 设计与实现》- 黄健宏
- 《Redis 深度历险:核心原理与应用实践》- 钱文品