Redis 内核

内存键值存储的数据结构服务器——ae 事件循环、SDS 动态字符串、RDB/AOF 持久化、主从复制。

概念

Redis 是一个内存数据结构服务器,用纯 C 编写 (约 12 万行)。它的”内核”是一个单线程事件循环处理所有客户端请求。Redis 的核心理念是:数据结构原生存储在内存中,网络 I/O 通过 epoll 多路复用,持久化通过 fork + Copy-on-Write 实现。Redis 是”如何用最少的代码构建一个高性能网络服务器”的范本。

核心组件

组件职责关键机制
ae 事件循环epoll/kqueue/select 驱动的异步 I/O文件事件 + 时间事件
SDSC 字符串的替代: O(1) 长度, 二进制安全, 预分配兼容 C 字符串 API
Dict哈希表: rehash 渐进式两个哈希表 rolling rehash
Skiplist有序集合 (ZSET) 的底层实现概率平衡替代红黑树
Ziplist/Listpack紧凑序列化存储: 连续内存, 省指针小型 list/hash/zset
RDB快照持久化: fork() → 子进程写全量数据Copy-on-Write
AOF增量持久化: 每条写命令追加到日志类似 WAL
复制/哨兵/集群高可用 + 水平扩展PSYNC, Raft

ae 事件循环

// ae 事件循环核心 (ae.c)
typedef struct aeEventLoop {
 aeFileEvent *events; // 注册的 fd 及其回调
 aeFiredEvent *fired; // epoll_wait 返回的就绪事件
 aeTimeEvent *timeEventHead; // 时间事件链表 (最近到期排最前)
};
 
void aeMain(aeEventLoop *eventLoop) {
 while (!eventLoop->stop) {
 // 计算离最近时间事件的毫秒数, 作为 epoll_wait 超时
 tv = shortestTimeEventTimeout(eventLoop);
 
 // 阻塞等待 I/O 事件 (epoll_wait)
 numevents = aeApiPoll(eventLoop, tv);
 
 // 处理所有就绪的文件事件
 for (i = 0; i < numevents; i++) {
 fileEvent->rfileProc(...); // 读事件回调
 fileEvent->wfileProc(...); // 写事件回调
 }
 
 // 处理所有到期的时间事件
 processTimeEvents(eventLoop);
 }
}

核心数据结构

// SDS -- Simple Dynamic String
struct sdshdr {
 int len; // 已使用长度 (O(1) strlen)
 int free; // 剩余空间 (减少 realloc)
 char buf[]; // 柔性数组, 实际数据
};
 
// Dict -- 渐近式 rehash 哈希表
struct dict {
 dictEntry **table[2]; // table[0] 是当前, table[1] 是 rehash 目标
 long rehashidx; // -1 表示未在 rehash
 // ...
};
// 每次 CRUD 操作附带移动 1 个桶, 分步完成 rehash
 
// Skiplist -- 有序集合的底层
struct zskiplistNode {
 sds ele; // 元素值
 double score; // 排序分数
 struct zskiplistLevel {
 struct zskiplistNode *forward; // 前向指针
 unsigned int span; // 跨度 (快速 rank 查询)
 } level[]; // 多层 index (随机层数, 概率 1/4)
};

RDB 持久化

主进程 (Redis Server):
 fork()
 |
 +-- 子进程:
 | 遍历所有 DB (共享内存 Copy-on-Write)
 | 将所有 key-value 序列化写入 dump.rdb
 | 退出
 |
 +-- 父进程:
 继续处理客户端请求
 如果父进程修改了数据 → 触发 Copy-on-Write → 子进程看到旧版本
 (fork 时刻的快照, 不是实时数据)

AOF 持久化

每条成功执行的写命令追加到 AOF 文件:
 SET key "hello" → AOF: *3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nhello\r\n

AOF 重写 (BGREWRITEAOF):
 在子进程中, 基于当前内存数据生成最小命令集合
 例如: 一条 RPUSH list A → RPUSH list B → RPUSH list C
 重写为: RPUSH list A B C (合并为一条)

主从复制

1. 从库连接主库, 发送 PSYNC <replication_id> <offset>
2. 主库检测是否可以部分同步 (offset 在复制缓冲区中)
 部分同步: 发送 offset 之后的增量命令
 全量同步: fork → 生成 RDB → 发送 RDB 给从库 → 发送缓冲区增量命令
3. 从库加载 RDB → 执行增量命令 → 进入实时同步
4. 主库每条写命令同时发送给所有已连接的从库