zset 的有序性、Stream 的消息 ID 索引、Redis Cluster 的 slot 映射——分别由 skiplist(跳跃表) 与 rax(基数树) 支撑。本文从源码视角解析:为什么 zset 不用红黑树、skiplist 的随机层级、rax 的压缩节点与 Stream 的底层组织。
| 需求 | 结构 | 复杂度 |
|---|---|---|
| 按成员查分数(ZSCORE) | dict | O(1) |
| 按分数范围遍历(ZRANGEBYSCORE) | skiplist | O(logN + M) |
| 按排名取(ZRANK) | skiplist(span 加速) | O(logN) |
typedef struct zset {
dict *dict; /* member → score 映射 */
zskiplist *zsl; /* score 有序索引 */
} zset;#define ZSKIPLIST_MAXLEVEL 64 /* 最大 64 层(7.0 起由 32 提升) */
#define ZSKIPLIST_P 0.25 /* 晋升概率 1/4 */
typedef struct zskiplistNode {
sds ele; /* 成员 */
double score; /* 分数 */
struct zskiplistNode *backward; /* 后退指针(第 1 层) */
struct zskiplistLevel {
struct zskiplistNode *forward; /* 前进指针 */
unsigned long span; /* 跨过多少节点(用于 ZRANK) */
} level[]; /* 柔性数组:每节点 1~64 层 */
} zskiplistNode;节点创建时,从第 1 层开始,以 1/4 概率逐层晋升,直到 64 层封顶(7.0 前 32 层):
int zslRandomLevel(void) {
int level = 1;
while ((random() & 0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
level += 1;
return (level < ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}span 字段记录"跨过的节点数",ZRANK 直接累加 span 得到排名,无需从 head 遍历。7.x 中小 zset(≤128 元素且值 ≤64B)用 listpack(见上一章),超过阈值才升级为 skiplist+dict——小数据用紧凑编码,大数据用高效索引,两级火箭。
| 用途 | 说明 |
|---|---|
| Stream 消息索引 | 按消息 ID(时间戳-序列号)组织,前缀共享 |
| 客户端缓存失效表 | CLIENT TRACKING 的前缀 → 客户端失效索引(6.0+) |
基数树是"按 key 前缀共享路径"的树:
typedef struct raxNode {
uint32_t iskey:1; /* 该节点是否是一个 key 的结尾 */
uint32_t isnull:1; /* value 是否为空 */
uint32_t iscompr:1; /* 是否压缩节点 */
uint32_t size:29; /* 子节点数(压缩节点=1) */
unsigned char data[];/* 存储字符 + 子节点指针 */
} raxNode;foo/for 共享 fo)——内存极致压缩;Stream 内部 = rax(按 ID 前缀索引)+ 每个节点一个 listpack(存同前缀段的消息):
Stream key
└── rax 树(key = 消息 ID 前缀,如 "1760000000123-")
└── listpack(存放该前缀下的多条消息)XADD O(logN)(rax 插入)+ O(1)(listpack 追加)。| 操作 | skiplist | rax | listpack |
|---|---|---|---|
| 单点查找 | O(logN) | O(前缀长) | O(N) |
| 范围查询 | O(logN + M) | O(前缀长 + M) | O(N) |
| 插入 | O(logN) | O(前缀长) | O(N)(memmove) |
| 内存 | 中(指针多) | 小(前缀共享) | 极小 |
| 适用 | zset 大集合 | Stream/前缀索引 | 小集合 |
OBJECT ENCODING 看到 skiplist 表示 zset 已大;listpack 表示仍在小对象区;本系列(01-28)从基础数据结构到内部实现,系统覆盖了 Redis 7.x 的核心知识体系:
| 模块 | 篇章 |
|---|---|
| 数据结构 | 01-06(基础结构、锁、队列、位图、HLL、Geo) |
| 核心机制 | 07-11(限流、管道事务、IO 模型、Scan、持久化) |
| 高可用 | 12-18(主从、Sentinel、Codis、Cluster) |
| 可靠消息 | 19(Stream) |
| 安全与运维 | 20-24(ACL/TLS、Java 客户端、命令行、Info 监控) |
| 内部实现 | 25-28(SDS、dict、listpack/quicklist、skiplist/rax) |
结合官方文档(redis.io)与源码阅读,这套体系可作为 Redis 从应用到源码的完整学习路径。