真实面经题目 · 原创解析

Redis ZSet 底层如何实现?

ZSet 的底层实现要抓住两点:它同时需要按 member 快速查分数,又需要按 score 有序范围查询。因此典型实现是字典加跳表,小规模时使用紧凑编码以节省内存。

出现于:字节跳动 · 后端开发

60 秒回答模板

Redis ZSet 是有序集合,每个 member 对应一个 score,member 唯一但 score 可以重复。为了同时支持快速按 member 查询和按 score 排序范围查询,ZSet 通常由 dict 和 skiplist 共同实现:dict 保存 member 到 score 的映射,查分数、判断成员是否存在接近 O(1);skiplist 按 score 排序,score 相同时再按 member 字典序排序,支持范围查询、排名、删除等操作,复杂度通常是 O(logN) 加返回元素数量。对于元素少、成员较短的小 ZSet,Redis 会使用 listpack 等紧凑编码节省内存,超过阈值后转换为跳表加字典结构。

考点 双结构核心
难度 真实面经高频题
回答目标 讲清机制、边界和追问

深入解析

01

ZSet 的语义要求

ZSet 既是集合,又带有排序能力。member 必须唯一,score 用来排序,多个 member 可以拥有相同 score。它要支持 ZSCORE 这种按 member 查分数,也要支持 ZRANGE、ZREVRANGE、ZRANGEBYSCORE 这类按顺序或分数区间取数据。如果只用哈希表,范围查询很差;如果只用有序结构,按 member 更新和查找又不够直接。

02

dict 负责快速定位

dict 的作用是保存 member 到 score 的映射,用于快速判断 member 是否存在、获取当前 score,以及更新分数时先找到旧值。这个结构让 ZSCORE、ZADD 判断覆盖、ZREM 定位成员等操作具备接近 O(1) 的查找能力。没有 dict 的话,每次按 member 操作都要在有序结构中查找,复杂度和实现成本都会明显上升。

03

skiplist 负责有序访问

skiplist 按 score 维护全局有序关系,score 相同则按 member 字典序打破平局。跳表通过多层前进指针把查找、插入、删除的期望复杂度控制在 O(logN),同时底层链表又天然适合顺序遍历区间结果。它比平衡树实现更简单,范围扫描也直接,因此很适合 ZSet 的排序和排名类命令。

04

小集合的紧凑编码

当 ZSet 元素数量少、member 长度也较小时,使用 dict 加 skiplist 的指针开销会显得过高。Redis 会用 listpack 这类紧凑连续内存编码保存 member-score 对,牺牲一定查找复杂度来换取更低内存占用和更好的缓存局部性。超过配置阈值后,再转换为更适合大数据量操作的跳表加字典结构。

05

更新和范围查询流程

执行 ZADD 时,Redis 会先通过 dict 判断 member 是否存在;如果是新成员,就插入 dict 和 skiplist;如果 score 改变,需要从 skiplist 删除旧节点再插入新节点,同时更新 dict。范围查询则主要走 skiplist,先定位起点,再沿底层链表顺序返回结果。两个结构必须同步维护,才能同时保证查询效率和排序正确性。

易错点

  • 只说 ZSet 用跳表,漏掉 dict 对 member 到 score 查询的作用。
  • 认为 score 必须唯一,实际上唯一的是 member,score 可以重复。
  • 把小 ZSet 的紧凑编码和大 ZSet 的跳表字典实现混为一谈。
  • 说跳表所有操作都是严格 O(1),忽略插入、删除、排名查询通常是 O(logN) 级别。

面试官追问

为什么 ZSet 不只用哈希表?

哈希表能快速按 member 查 score,但不能高效维护 score 顺序。范围查询、排名查询需要排序能力,只用哈希表会退化为全量扫描再排序,成本很高。

为什么 ZSet 不只用跳表?

跳表适合按 score 查找和范围遍历,但按 member 直接定位不如哈希表方便。ZSet 经常需要判断成员是否存在和读取旧 score,所以需要 dict 补足这条访问路径。

跳表里 score 相同怎么办?

score 相同时会继续按 member 的字典序比较,形成确定的全序关系。这样即使多个元素分数一样,范围遍历和排名结果也可以稳定定义。

ZSet 的 ZADD 复杂度是多少?

大集合编码下,查 member 依赖 dict 接近 O(1),插入或更新 skiplist 通常是 O(logN)。如果返回或处理多个元素,还要加上结果数量带来的线性成本。