Redis 网络模型详解
1. Redis 网络模型概述
核心特点:
- 单线程 Reactor 模式 (Redis 6.0 前)
- 多线程 I/O 处理 (Redis 6.0+)
- 基于事件驱动 (epoll/kqueue/select)
- 高性能关键设计:
- 单线程避免锁竞争
- 纯内存操作
- 非阻塞 I/O
2. Redis 6.0 前的单线程模型
工作流程:
初始化阶段:
- 创建监听套接字
- 绑定事件处理器(acceptTcpHandler)
- 注册到事件循环(aeMain)
事件循环流程:
[客户端请求]
→ [内核接收]
→ [epoll_wait 返回可读事件]
→ [Redis 主线程顺序处理命令]
→ [写回响应]
关键源码结构:
// 事件循环核心结构
typedef struct aeEventLoop {
int maxfd;
aeFileEvent *events; // 注册的事件
aeFiredEvent *fired; // 就绪的事件
aeTimeEvent *timeEventHead; // 时间事件
} aeEventLoop;
// 事件处理器
typedef void aeFileProc(struct aeEventLoop *eventLoop,
int fd, void *clientData, int mask);
优点:
- 无锁设计,避免竞争
- 顺序执行,避免上下文切换
- 实现简单,易于维护
缺点:
- 单线程无法利用多核CPU
- 大键删除等操作会阻塞
3. Redis 6.0+ 的多线程模型
架构改进:
- 主线程:负责命令执行和事件调度
- I/O 线程:处理网络读写(默认4个)
- 后台线程:处理惰性删除等任务
工作流程:
- 主线程接收连接请求
- 将就绪的socket分发给I/O线程
- I/O线程并行读取请求并解析
- 主线程串行执行命令
- I/O线程并行写回响应
关键配置:
io-threads 4
# I/O线程数
io-threads-do-reads yes
# 是否启用读多线程
线程分工图示:
┌───────────────────────────────────────────────────────┐
│ 客户端请求队列 │
└───────────────┬───────────────────┬───────────────────┘
│ │
▼ ▼
┌───────────────────────┐ ┌───────────────────────┐
│ 主线程 │ │ I/O线程组 │
│ │ │ (默认4个,可配置) │
│ 1. 接收新连接 │ │ │
│ 2. 分发就绪socket │ │ 3. 并行读取请求数据 │
│ 6. 执行命令 │ │ 4. 解析协议 │
│ 7. 准备响应 │ │ 8. 并行发送响应 │
│ │ │ │
└───────────┬───────────┘ └───────────┬───────────┘
│ │
│ 5. 将解析好的命令 │
└───────────>───────────────┘
典型部署架构
┌───────────────┐
│ 客户端 │
└──────┬───────┬┘
│ │
┌──────▼─────┐ │
│ Redis │ │
│ Proxy层 │ │
└──────┬─────┘ │
│ │
┌───────▼┐ ┌──▼───────┐
│ Redis │ │ Redis │
│ Master │ │ Replica │
└─────────┘ └──────────┘
4. 事件处理机制
事件类型:
文件事件 (套接字读写)
- 使用epoll/kqueue实现
- 响应式处理网络请求
时间事件 (定时任务)
- 周期性事件(如serverCron)
- 单次定时事件
事件处理器注册:
// 示例:注册客户端连接处理器
aeCreateFileEvent(server.el, server.ipfd, AE_READABLE,
acceptTcpHandler,NULL);
事件循环核心逻辑:
void aeMain(aeEventLoop *eventLoop) {
while (!eventLoop->stop) {
aeProcessEvents(eventLoop, AE_ALL_EVENTS);
}
}
5. 高性能网络设计要点
I/O 多路复用:
- Linux 使用 epoll
- macOS 使用 kqueue
- 其他平台 select
连接处理优化:
- 可配置的最大连接数(maxclients)
- 客户端缓冲区管理
- 协议解析优化
网络参数调优:
tcp-backlog 511 # TCP backlog队列
tcp-keepalive 300 # 保活检测
repl-disable-tcp-nodelay no # 禁用Nagle算法
6. 请求处理全流程
连接建立:
- acceptTcpHandler → createClient
- 初始化客户端状态
命令读取:
- readQueryFromClient
- 解析Redis协议
命令执行:
- processCommand → call
- 执行前检查:
- 内存限制
- 命令权限
- 集群重定向
响应返回:
- addReply → _addReplyToBuffer
- 写入客户端输出缓冲区
连接关闭:
- freeClientAsync
- 异步释放资源
7. 多线程模型源码分析
I/O 线程初始化:
void initThreadedIO(void) {
io_threads_active = 0;
for (int i = 0; i < server.io_threads_num; i++) {
pthread_create(&io_threads[i],NULL,
IOThreadMain,(void*)(long)i);
}
}
I/O 线程主逻辑:
void *IOThreadMain(void *myid) {
while(1) {
for (int j = 0; j < io_threads_list_len; j++) {
redisAtomicIncr(io_threads_pending[myid],1);
// 执行读/写操作
}
}
}
8. 性能对比数据
场景 | 单线程 QPS | 多线程(4) QPS |
---|---|---|
GET 操作 | 120,000 | 210,000 |
SET 操作 | 110,000 | 190,000 |
混合读写 | 100,000 | 180,000 |
9. 生产环境调优建议
网络配置:
- 适当增加io-threads(不超过CPU核心数)
- 调整TCP backlog
监控指标:
- instantaneous_ops_per_sec
- blocked_clients
- rejected_connections
问题排查:
- 慢查询监控(slowlog)
- 客户端缓冲区监控
- 连接数监控
10. 典型问题解决方案
连接数暴涨:
- 检查客户端连接池配置
- 设置合理的timeout
- 使用CLIENT KILL命令清理
网络延迟高:
- 启用TCP keepalive
- 检查网络硬件
- 考虑部署Proxy
CPU 瓶颈:
- 升级到Redis 6.0+
- 增加I/O线程
- 优化大key操作