一、Redis 的核心设计哲学
Redis 的成功源于其 「用内存换时间」 的核心理念,围绕以下三个核心原则构建:
-
极简主义:单线程模型避免锁竞争,代码保持高度内聚。
-
性能至上:所有数据常驻内存,网络层采用事件驱动模型。
-
可扩展性:模块化设计,通过插件机制支持新功能。
二、源码目录结构解析
从 Redis 7.0 源码看核心模块:
bash
redis/src
├── ae.c # 事件循环(核心)
├── anet.c # 网络抽象层
├── dict.c # 哈希表实现
├── object.c # 数据类型封装
├── rdb.c # RDB持久化
├── aof.c # AOF持久化
├── server.c # 服务端主逻辑
├── networking.c # 客户端连接处理
└── modules # 模块化扩展
三、核心模块源码深度解析
1. 事件驱动模型:单线程为何能扛10万QPS?
Redis 采用 Reactor 模式 实现高并发,核心代码在 ae.c
:
cpp
// 事件循环主逻辑(ae.c)
void aeMain(aeEventLoop *eventLoop) {
eventLoop->stop = 0;
while (!eventLoop->stop) {
aeProcessEvents(eventLoop, AE_ALL_EVENTS | AE_CALL_BEFORE_SLEEP);
}
}
// 处理事件(精简版)
int aeProcessEvents(aeEventLoop *eventLoop, int flags) {
// 1. 获取最近要到期的时间事件
shortest = aeSearchNearestTimer(eventLoop);
// 2. 等待文件事件(epoll_wait/kevent等)
numevents = aeApiPoll(eventLoop, tvp);
// 3. 处理文件事件(网络请求)
for (j = 0; j < numevents; j++) {
fe = &eventLoop->events[eventLoop->fired[j].fd];
fe->rfileProc(...); // 处理读事件
fe->wfileProc(...); // 处理写事件
}
// 4. 处理时间事件(如过期键清理)
processed += processTimeEvents(eventLoop);
}
技术要点:
-
基于
epoll/kqueue
实现多路复用 -
单线程顺序处理事件,避免锁开销
-
时间事件与文件事件协同调度
2. 内存管理:如何实现高效数据存储?
核心结构 redisObject
(object.c):
cpp
typedef struct redisObject {
unsigned type:4; // 数据类型(string/hash等)
unsigned encoding:4; // 编码方式(优化存储)
unsigned lru:24; // LRU时间戳
int refcount; // 引用计数
void *ptr; // 数据指针
} robj;
// 字符串类型示例(sds.h)
struct sdshdr {
int len; // 已用长度
int free; // 剩余空间
char buf[]; // 柔性数组
};
编码优化策略:
-
String
:int编码(存储整数时) vs embstr编码(短字符串) vs raw编码 -
Hash
:ziplist(元素少时) vs hashtable -
Sorted Set
:skiplist + dict 实现 O(logN) 查询
3. 持久化机制:RDB与AOF如何协同工作?
RDB 快照生成(rdb.c):
cpp
// 异步生成RDB(fork子进程)
int rdbSaveBackground(char *filename, rdbSaveInfo *rsi) {
if ((childpid = fork()) == 0) {
// 子进程执行实际保存
retval = rdbSave(filename,rsi);
exitFromChild((retval == C_OK) ? 0 : 1);
} else {
// 父进程记录状态
server.rdb_child_pid = childpid;
}
}
AOF 重写(aof.c):
cpp
// 重写AOF文件(同样fork子进程)
int rewriteAppendOnlyFileBackground(void) {
if (aofCreatePipes() != C_OK) return C_ERR;
if ((childpid = fork()) == 0) {
// 子进程重写
if (rewriteAppendOnlyFile(tmpfile) == C_OK) {
exitFromChild(0);
}
}
}
持久化流程对比:
RDB | AOF | |
---|---|---|
原理 | 内存快照 | 操作日志追加 |
优点 | 恢复快、文件小 | 数据丢失风险低 |
缺点 | 数据可能丢失 | 文件大、恢复慢 |
4. 数据结构:Sorted Set 如何实现高效排序?
核心代码 t_zset.c
:
cpp
// 跳跃表节点定义
typedef struct zskiplistNode {
sds ele; // 成员
double score; // 分数
struct zskiplistNode *backward; // 后退指针
struct zskiplistLevel {
struct zskiplistNode *forward; // 前进指针
unsigned long span; // 跨度
} level[]; // 层级数组
} zskiplistNode;
// 插入节点核心逻辑
zskiplistNode *zslInsert(zskiplist *zsl, double score, sds ele) {
// 1. 随机生成节点层级(幂次定律)
level = zslRandomLevel();
// 2. 查找插入位置
for (i = zsl->level-1; i >= 0; i--) {
while (x->level[i].forward &&
(x->level[i].forward->score < score || ... )) {
x = x->level[i].forward;
}
update[i] = x;
}
// 3. 创建新节点并调整指针
x = zslCreateNode(level,score,ele);
for (i = 0; i < level; i++) {
x->level[i].forward = update[i]->level[i].forward;
update[i]->level[i].forward = x;
}
}
性能优势:
-
跳跃表平均 O(logN) 时间复杂度
-
结合哈希表实现 O(1) 成员存在性检查
四、企业级应用源码适配案例
案例1:分布式锁实现(Redlock算法)
cpp
// 加锁命令(基于SET命令)
SET lock_key $unique_id NX PX 30000
// 解锁Lua脚本(保证原子性)
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
源码支撑:
-
SET
命令在t_string.c
实现 -
Lua 脚本执行逻辑在
scripting.c
案例2:热点数据缓存穿透防御
cpp
// 布隆过滤器实现(redisbloom模块)
BF.ADD hot_items:filter "item_123"
BF.EXISTS hot_items:filter "item_456"
源码扩展:
-
模块化开发接口在
redismodule.h
-
底层使用
dablooms
库实现
五、Redis 核心架构图
bash
+-------------------+
| Client |
+-------------------+
|
| 发送命令
v
+-------------------+
| Event Loop | <--- 文件事件(网络I/O)
| (ae.c/ae_epoll.c) |
| | <--- 时间事件(过期键清理)
+-------------------+
|
| 命令路由
v
+-------------------+
| Command Table | ---> 命令处理器(get/set等)
| (server.c/cmd) |
+-------------------+
|
| 数据操作
v
+-------------------+
| Data Store | ---> 字符串/hash/有序集合等
| (object.c/t_*.c) |
+-------------------+
|
| 持久化触发
v
+-------------------+
| Persistence | ---> RDB(rdb.c) / AOF(aof.c)
+-------------------+
六、Redis 的局限与优化方向
-
内存限制:可通过 Redis Cluster 分片扩展
-
单线程瓶颈:Redis 6.0 引入多线程I/O(仍保持命令处理单线程)
-
持久化风险:建议 RDB+AOF 混合使用,定期备份
-
扩展性:通过 Module 机制集成新功能(如 RedisSearch)
七、总结
通过源码分析可深入理解 Redis 的 高性能设计精髓:
-
事件驱动模型:单线程扛高并发
-
内存数据结构:精心优化的编码方式
-
可扩展架构:模块化设计支持二次开发
企业级应用建议:
-
性能敏感场景:结合跳跃表、哈希表特性设计数据结构
-
高可用要求:部署 Redis Cluster + Sentinel 监控
-
混合持久化:RDB 定期快照 + AOF 实时日志