Redis 启动运行流程图
graph TD
A[启动Redis服务器] --> B[初始化服务器状态]
B --> C[加载配置]
C --> D[创建事件循环 aeEventLoop]
D --> E[绑定监听端口]
E --> F[注册时间事件]
F --> G[注册文件事件:监听套接字]
G --> H[进入主事件循环 aeMain]
subgraph 事件循环
H --> I[aeProcessEvents]
I --> J{阻塞等待事件}
J -->|文件事件/时间事件| K[处理事件]
end
subgraph 文件事件处理
K --> L{事件类型}
L -->|监听套接字可读| M[接受新连接 acceptTcpHandler]
M --> N[创建客户端 redisClient]
N --> O[注册客户端套接字读事件]
L -->|客户端套接字可读| P[读取请求 readQueryFromClient]
P --> Q[解析命令到 querybuf]
Q --> R[处理命令 processInputBuffer]
R --> S[执行命令 processCommand]
S --> T[命令执行器 call]
L -->|客户端套接字可写| U[发送响应 sendReplyToClient]
end
subgraph 命令执行
T --> V[查找命令字典 commandTable]
V --> W[执行命令实现函数]
W --> X[修改数据]
X --> Y[写入响应缓冲区]
Y --> Z[注册写事件]
end
subgraph 时间事件处理
K --> AA[执行 serverCron 任务]
AA --> AB[清理过期键]
AA --> AC[持久化操作]
AA --> AD[主从复制]
AA --> AE[集群心跳]
AA --> AF[统计信息更新]
end
Z --> H
U --> H
AE --> H
关键流程说明
-
初始化阶段
- 加载配置文件(
redis.conf) - 创建全局状态对象
redisServer - 初始化事件循环(
aeEventLoop) - 绑定监听端口(默认 6379)
- 加载持久化数据(RDB/AOF)
- 加载配置文件(
-
事件驱动核心
- 使用 I/O 多路复用(epoll/kqueue/select)
- 两种事件类型:
- 文件事件:网络 I/O(客户端连接/请求)
- 时间事件:定时任务(
serverCron)
-
客户端连接处理
// 伪代码示例 void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) { int cfd = accept(fd, &cliaddr, &clilen); // 接受连接 redisClient *c = createClient(cfd); // 创建客户端对象 aeCreateFileEvent(el, cfd, AE_READABLE, readQueryFromClient, c); // 注册读事件 } -
命令处理流程
- 读取请求:
readQueryFromClient()→ 填充querybuf - 协议解析:
processInputBuffer()→ 解析 Redis 协议 - 命令查找:
lookupCommand()在commandTable中查找 - 执行命令:通过
call()调用命令函数(如setCommand,getCommand)
- 读取请求:
-
时间事件(serverCron)
int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) { // 每100ms执行一次 databasesCron(); // 过期键清理 triggerPersist(); // 触发RDB/AOF持久化 replicationCron(); // 主从复制 clusterCron(); // 集群管理 updateStats(); // 更新统计信息 } -
响应返回
- 结果写入
buf和reply缓冲区 - 注册写事件 →
sendReplyToClient()异步发送
- 结果写入
-
多线程扩展(Redis 6.0+)
Main Thread: 接收连接 → 命令解析 → 命令执行 → 返回响应 ↑ IO Threads: 负责网络读/写(并行处理)
Redis处理客户端请求的流程
graph TD
A[客户端连接] --> B[TCP连接建立]
B --> C[创建client对象]
C --> D[注册到I/O多路复用器]
subgraph 事件循环
D --> E{等待事件}
E -->|读事件| F[读取数据到输入缓冲区]
F --> G[解析RESP协议]
G --> H{命令完整?}
H -->|否| E
H -->|是| I[查找命令处理器]
I --> J[执行命令]
J --> K[操作内存数据库]
K --> L[写入输出缓冲区]
L --> M[注册写事件]
E -->|写事件| N[发送响应数据]
N --> O{发送完成?}
O -->|否| E
O -->|是| P[重置缓冲区]
P --> Q{连接保持?}
Q -->|是| E
Q -->|否| R[关闭连接]
end
J -->|写命令| S[AOF持久化]
S --> L
J -->|主节点| T[复制传播]
T --> L
核心流程说明:
-
连接建立阶段
- 客户端通过TCP三次握手连接(端口6379)
- Redis创建
client对象(包含:socket fd、输入/输出缓冲区、状态标志) - 将socket注册到I/O多路复用器(epoll/kqueue)监听读事件
-
请求处理阶段
- 读事件触发:从socket读取数据到
querybuf(动态字符串实现) - 协议解析:按照RESP格式解析命令(支持Inline或Array格式)
// RESP数组示例:*2\r\n$3\r\nGET\r\n$5\r\nmykey\r\n - 命令分发:在命令表(
commandTable)中查找对应处理器
- 读事件触发:从socket读取数据到
-
命令执行阶段
- 核心操作:访问内存数据库(
redisDb字典结构)- 读操作:直接访问dict获取数据
- 写操作:修改dict并记录到AOF/传播到副本
- 内存管理:使用jemalloc分配,淘汰策略(LRU/LFU)
- 原子保证:单线程执行,无竞态条件
- 核心操作:访问内存数据库(
-
响应返回阶段
- 写事件触发:将
buf或reply链表中的数据发送 - 分块发送:网络拥塞时部分发送,保留剩余数据
- 连接复用:重置缓冲区保留连接(keep-alive)
- 写事件触发:将
-
后台联动
- 持久化:写命令触发AOF追加(
write或fsync) - 主从复制:主节点将写命令传播到副本
- 过期处理:访问时惰性删除+定期扫描
- 持久化:写命令触发AOF追加(
Redis持久化操作流程图
graph TD
A[Redis持久化操作] --> B[RDB持久化]
A --> C[AOF持久化]
%% RDB持久化流程
B --> D1{触发条件}
D1 --> |手动触发| D2[执行SAVE命令]
D1 --> |手动触发| D3[执行BGSAVE命令]
D1 --> |自动触发| D4[配置文件save规则满足]
D2 --> D5[阻塞主进程<br>创建RDB文件]
D3 --> D6[Fork子进程]
D6 --> D7[子进程创建临时RDB文件]
D7 --> D8[替换旧RDB文件]
D8 --> D9[发送信号给主进程]
D4 --> D6
%% AOF持久化流程
C --> E1[命令追加]
E1 --> |写命令| E2[追加到AOF缓冲区]
E2 --> E3{写入策略}
E3 --> |appendfsync always| E4[每个命令同步写盘]
E3 --> |appendfsync everysec| E5[每秒同步写盘]
E3 --> |appendfsync no| E6[由系统决定]
E4 --> E7[阻塞直到完成]
E5 --> E8[异步线程执行]
E6 --> E9[系统异步执行]
E7/E8/E9 --> E10[AOF文件增长]
E10 --> E11[AOF重写触发]
E11 --> E12[Fork子进程]
E12 --> E13[扫描内存数据]
E13 --> E14[构建新AOF临时文件]
E14 --> E15[替换旧AOF文件]
流程说明
RDB持久化流程:
-
触发条件:
- 手动触发:
SAVE(同步阻塞)或BGSAVE(异步) - 自动触发:满足
redis.conf中save <seconds> <changes>规则
- 手动触发:
-
BGSAVE执行:
graph LR M[主进程] --> F[Fork子进程] F --> C[子进程创建temp.rdb] C --> W[写入内存数据快照] W --> R[重命名为dump.rdb] R --> S[发送完成信号] S --> M- 子进程完成前,新BGSAVE请求会被拒绝
- 写时复制(COW)机制保证数据一致性
-
RDB文件结构:
+---------------------+ | REDIS | RDB版本 | 数据区 | EOF | 校验和 | +---------------------+
AOF持久化流程:
- 命令追加:
- 所有写命令追加到
aof_buf缓冲区
- 所有写命令追加到
- 写入策略:graph TB B[缓冲区] --> A[always] B --> E[everysec] B --> N[no] A -->|同步阻塞| D[磁盘] E -->|每秒异步| D N -->|系统异步| D
- AOF重写:
- 触发条件:文件增长超过阈值(auto-aof-rewrite-percentage)
- 重写过程:
- Fork子进程扫描内存数据
- 生成新AOF临时文件(无冗余命令)
- 重命名替换旧文件(主进程继续追加到新缓冲区)
关键配置参数
| 持久化类型 | 配置项 | 默认值 | 说明 |
|---|---|---|---|
| RDB | save 900 1 |
多组规则 | 自动触发条件 |
dbfilename dump.rdb |
dump.rdb | RDB文件名 | |
| AOF | appendonly yes |
no | 启用AOF |
appendfsync everysec |
everysec | 写入策略 | |
auto-aof-rewrite-percentage 100 |
100% | 重写触发条件(增长比例) |
混合持久化(Redis 4.0+)
graph LR
H[混合持久化] --> I[RDB头部]
H --> J[AOF尾部]
K[加载时] --> L[先加载RDB部分]
L --> M[重放AOF命令]
Redis清理过期键的详细运行流程图
graph TD
A[开始] --> B{键访问操作?}
B -->|是| C[惰性删除流程]
B -->|否| D[定期删除流程]
subgraph 惰性删除流程
C --> C1[执行命令前检查键是否过期]
C1 --> C2{键已过期?}
C2 -->|是| C3[异步删除键]
C3 --> C4[向客户端返回空值]
C2 -->|否| C5[正常执行命令]
end
subgraph 定期删除流程
D --> D1[定时任务启动]
D1 --> D2{是否达到执行周期?}
D2 -->|是| D3[随机抽取20个带TTL的键]
D3 --> D4[删除本轮发现的过期键]
D4 --> D5{删除比例>25%?}
D5 -->|是| D6[继续抽样下一批]
D5 -->|否| D7{达到时间上限?}
D7 -->|是| D8[结束本轮清理]
D7 -->|否| D3
D6 --> D9{已检查500键?}
D9 -->|是| D8
D9 -->|否| D3
D8 --> D10[等待下一周期]
end
关键机制说明:
-
惰性删除(被动清理):
- 触发条件:任何键访问操作(GET/SET等)
- 检查时机:命令执行前
- 执行动作:若键过期则异步删除
- 响应处理:向客户端返回空值(模拟键不存在)
-
定期删除(主动清理):
- 触发条件:Redis定时任务(默认每秒10次)
- 抽样规则:
- 每轮随机抽取20个带TTL的键
- 优先检查即将过期的键
- 渐进式清理:
- 当单轮删除比例 > 25% 时继续抽样
- 单轮最大检查500个键
- 单轮最大耗时25ms(避免阻塞主线程)
- 退出条件:
- 删除比例 ≤ 25%
- 或达到时间上限
- 或检查足够数量键
Redis主从复制的详细运行流程图
graph TD
A[从服务器启动] --> B[发送 SLAVEOF 命令]
B --> C[主服务器接收复制请求]
C --> D{是否首次同步?}
D -- 是 --> E[主进程 fork 子进程]
E --> F[生成 RDB 快照]
F --> G[缓存写命令到复制缓冲区]
G --> H[发送 RDB 文件给从服务器]
H --> I[从服务器清空旧数据]
I --> J[加载 RDB 恢复数据]
J --> K[发送复制缓冲区命令]
K --> L[从服务器执行增量命令]
D -- 否 --> M[检查 Replication ID/Offset]
M --> N{偏移量是否在<br>复制缓冲区?}
N -- 是 --> O[发送部分同步命令]
O --> L
N -- 否 --> P[触发全量同步]
P --> E
L --> Q[持续命令传播]
Q --> R[主服务器实时发送写命令]
R --> S[从服务器顺序执行命令]
S --> T[保持数据最终一致]
classDef highlight fill:#f9f,stroke:#333,stroke-width:2px;
class E,F,G,H,J,K,O,P,R,S highlight;
关键流程说明:
-
初始化连接
- 从服务器启动时执行
SLAVEOF master_ip master_port - 主服务器创建从服务器套接字并发送
PING验证连接
- 从服务器启动时执行
-
全量复制(首次同步)
graph LR E[主进程 fork] --> F[BGSAVE 生成 RDB] F --> G[缓存新写命令到复制缓冲区] G --> H[传输 RDB 文件] H --> J[从服务器 FLUSHDB 后加载 RDB]- 核心细节:
- RDB 生成期间主库继续处理命令
- 复制缓冲区大小配置:
client-output-buffer-limit slave - 传输使用专用通道(不阻塞主线程)
- 核心细节:
-
增量复制
graph LR K[主服务器发送缓冲区命令] --> L[从服务器执行命令] L --> Q[进入稳定复制状态]- 复制原理:
- 主服务器维护
Replication Stream(命令流) - 从服务器记录复制偏移量(
master_repl_offset)
- 主服务器维护
- 复制原理:
-
断线重连
graph TB M{检查 Replication ID} -->|相同| N[检查复制偏移量] N -->|偏移量在缓冲区内| O[发送 PSYNC 部分同步] N -->|偏移量丢失| P[触发全量同步]- 关键参数:
repl-backlog-size(缓冲区大小,默认1MB)master_replid(复制ID)
- 关键参数:
-
持续同步
- 主服务器每执行写命令,异步推送给从服务器
- 从服务器按顺序执行命令(单线程)
Redis集群心跳发送的运行流程图
graph TD
A[Redis节点启动定时心跳任务] --> B[心跳触发]
B --> C[随机选择集群中的N个节点]
C --> D1[目标节点1]
C --> D2[目标节点2]
C --> D3[...]
C --> DN[目标节点N]
subgraph 心跳发送与处理
D1 --> E1[发送PING消息<br>携带自身状态+部分节点状态]
D2 --> E2[发送PING消息<br>携带自身状态+部分节点状态]
DN --> EN[发送PING消息<br>携带自身状态+部分节点状态]
E1 --> F1[接收方验证消息]
E2 --> F2[接收方验证消息]
EN --> FN[接收方验证消息]
F1 --> G1[更新本地集群状态]
F2 --> G2[更新本地集群状态]
FN --> GN[更新本地集群状态]
G1 --> H1[回复PONG消息]
G2 --> H2[回复PONG消息]
GN --> HN[回复PONG消息]
H1 --> I1[发送方接收PONG]
H2 --> I2[发送方接收PONG]
HN --> IN[发送方接收PONG]
I1 --> J1[更新本地集群状态]
I2 --> J2[更新本地集群状态]
IN --> JN[更新本地集群状态]
end
J1 --> K[判断节点状态变化]
J2 --> K
JN --> K
K --> L{故障检测?}
L -->|节点下线| M[标记疑似下线<br>PFail状态]
L -->|超时未响应| M
L -->|正常| N[更新心跳时间戳]
M --> O[广播节点状态]
O --> P[其他节点接收状态]
P --> Q[收集故障报告]
Q --> R{达到仲裁数?}
R -->|是| S[标记实际下线<br>Fail状态]
R -->|否| T[保持PFail状态]
S --> U[触发故障转移]
T --> V[继续监听]
style A fill:#f9f,stroke:#333
style K fill:#ff9,stroke:#333
style M fill:#f96,stroke:#333
style S fill:#f44336,stroke:#333
style U fill:#4CAF50,stroke:#333
Redis集群心跳运行流程详解:
- 定时触发:每个节点启动定时任务(默认每秒10次)
- 节点选择:每次随机选择集群节点总数的1/10(至少3个节点)
- PING发送:
- 携带自身状态信息
- 附带已知的其他节点状态(Gossip协议)
- 接收处理:
- 接收方验证消息有效性
- 更新本地集群拓扑状态
- 回复PONG消息(含自身状态)
- 状态更新:
- 发送方收到PONG后更新节点状态
- 记录最新心跳时间戳
- 故障检测:
- 超时未响应节点标记为PFail(疑似下线)
- 广播故障信息给其他节点
- 故障仲裁:
- 当多数主节点确认故障时
- 节点标记为Fail(实际下线)
- 故障转移:
- 触发从节点晋升机制
- 保证集群高可用
Redis统计信息更新运行流程图
flowchart TD
A[Redis统计信息更新流程] --> B[客户端发送命令]
B --> C[接收命令请求]
C --> D[解析命令]
D --> E[更新命令统计]
E -->|更新命令调用次数| F[commandstats]
E -->|更新总命令计数器| G[total_commands_processed]
D --> H[执行命令]
H --> I[更新内存统计]
I -->|更新内存使用量| J[used_memory]
I -->|更新峰值内存| K[used_memory_peak]
H --> L[更新Keyspace统计]
L -->|更新键数量| M[db0:keys]
L -->|更新过期键数量| N[db0:expires]
H --> O[更新网络统计]
O -->|更新输入流量| P[total_net_input_bytes]
O -->|更新输出流量| Q[total_net_output_bytes]
H --> R[更新CPU统计]
R -->|更新系统CPU时间| S[used_cpu_sys]
R -->|更新用户CPU时间| T[used_cpu_user]
B --> U[更新连接统计]
U -->|更新总连接数| V[total_connections_received]
U -->|更新当前连接数| W[connected_clients]
H --> X[返回响应]
X --> Y[更新请求延迟]
Y -->|计算P50/P95/P99| Z[latency_percentiles]
style A fill:#f9f,stroke:#333,stroke-width:2px
流程说明:
-
命令接收阶段
- 客户端发送命令请求
- Redis接收并解析命令
- 更新全局命令计数器:
total_commands_processed++ - 更新命令专属计数器:
commandstats[cmd_name].calls++
-
命令执行阶段
- 内存统计:
- 动态更新内存使用量(
used_memory) - 记录峰值内存(
used_memory_peak)
- 动态更新内存使用量(
- Keyspace统计:
- 更新键数量(
db0:keys) - 更新过期键数量(
db0:expires)
- 更新键数量(
- 网络统计:
- 累计输入流量(
total_net_input_bytes) - 累计输出流量(
total_net_output_bytes)
- 累计输入流量(
- CPU统计:
- 记录系统CPU时间(
used_cpu_sys) - 记录用户CPU时间(
used_cpu_user)
- 记录系统CPU时间(
- 内存统计:
-
连接管理
- 连接建立时:
total_connections_received++ - 实时维护:
connected_clients
- 连接建立时:
-
响应阶段
- 计算请求延迟百分位值(P50/P95/P99)
- 更新延迟统计(
latency_percentiles)