当前位置: 首页 > news >正文

实战探讨:为什么 Redis Zset 选择跳表?

在了解了跳表的原理和实现后,一个常见的问题(尤其是在面试中)随之而来:为什么像 Redis 的有序集合 (Zset) 这样的高性能组件会选择使用跳表,而不是大家熟知的平衡树(如红黑树)呢?

对于这个问题,Redis 的作者 Salvatore Sanfilippo (@antirez) 曾给出过解释,主要可以归纳为以下几点:

  1. 内存效率与灵活性 (Memory Efficiency & Flexibility):

    • 跳表并非特别消耗内存。其内存占用可以通过调整节点提升的概率 p​ 来控制。通过选择合适的 p​ 值,可以使得跳表的平均内存占用低于某些平衡树。
  2. 高效的范围查询与缓存局部性 (Efficient Range Queries & Cache Locality):

    • Zset 经常需要执行 ZRANGE​ (按排名范围查询) 或 ZREVRANGE​ (按排名反向范围查询) 操作,这本质上是在有序结构上进行一段连续元素的遍历。
    • 跳表的最底层 (Level 0) 是一个有序链表。执行范围查询时,只需定位到范围的起始点,然后沿着 Level 0 的链表顺序遍历即可。这种顺序访问模式具有良好的缓存局部性 (cache locality),与平衡树的中序遍历相比,至少同样好,甚至可能更好。
  3. 实现的简单性与易扩展性 (Implementation Simplicity & Extensibility):

    • 跳表的实现、调试相比平衡树(尤其是红黑树)要简单得多。平衡树复杂的旋转和重新平衡逻辑很容易出错。
    • 跳表的简单性也带来了更好的可扩展性。@antirez 提到,得益于跳表的简洁,他很容易地集成了一个社区贡献的补丁,通过对跳表进行少量修改,就在 O(log N) 时间内实现了 ZRANK​ (获取成员排名) 的功能。

进一步解读与补充:

  • 内存占用对比:

    • 平衡树(如红黑树)每个节点通常需要存储 2 个指向子节点的指针,以及可能的父指针和颜色信息。
    • 跳表每个节点包含的指针数目是可变的,取决于它提升的层数。平均来说,每个节点包含的指针数量为 1 / (1 - p)​(其中 p​ 是节点提升一级概率)。在 Redis 的实现中,p​ 通常取 0.25 (1/4),这意味着平均每个节点大约有 1 / (1 - 0.25) = 1.33​ 个 right​ 指针(再加上 down​ 指针和可能的 backward​ 指针,但核心指向下一节点的指针数平均较少)。这使得跳表在内存使用上具有一定的灵活性和潜在优势。
  • 范围查询的易实现性:

    • 如 @antirez 所说,跳表执行范围查询非常自然。找到范围起点后,沿着最底层的链表顺序遍历即可,逻辑简单清晰。
    • 平衡树执行范围查询,需要先找到范围起点,然后执行中序遍历来依次访问后续节点,直到超出范围终点。虽然中序遍历本身不复杂,但在某些实现中,高效地进行部分中序遍历可能需要额外的辅助结构或递归,相对跳表的直接链表遍历要稍微复杂一些。此外,跳表 Level 0 的节点在内存中可能是物理上更连续的(如果内存分配器配合),有利于缓存;而树的中序遍历则可能在内存地址上跳跃。
  • 实现与维护成本:

    • 这一点对于像 Redis 这样需要高性能、高稳定性且持续迭代的项目至关重要。更简单的实现意味着更少的 Bug、更快的开发迭代速度和更低的维护成本。平衡树,特别是红黑树,其插入和删除操作涉及多种情况的判断、旋转和重新染色,逻辑复杂,容易出错。

总结来说,Redis Zset 选择跳表是基于其在内存占用、范围查询性能与实现简洁性之间取得的良好平衡。 对于 Zset 这种既需要快速单点查找/更新,又需要高效范围遍历的场景,跳表提供了一个非常实用且工程上更优的解决方案。

相关文章:

  • UDP / TCP 协议
  • 机器学习入门-线性回归模型/损失函数/梯度下降
  • Java SE(7)——类和对象(二)
  • 深入解析 SqlSugar 与泛型封装:实现通用数据访问层
  • 4.29-4.30 Maven+单元测试
  • Spring MVC @RequestBody 注解怎么用?接收什么格式的数据?
  • 数据赋能(208)——质量管理——及时性原则
  • 单细胞测序数据分析流程的最佳实践
  • 旋转矩阵公式理解
  • Learning vtkjs之PolyDataNormals
  • Android Compose 中 Side Effects 和 State 相关的 API 使用
  • Python datetime库的用法 Python从入门到入土系列第3篇-洞察标准库DateTime
  • 第九章:反击的序曲(续)
  • 优化01-统计信息
  • Space Engineers 太空工程师 [DLC 解锁] [Steam] [Windows]
  • 生成器模式(Builder Pattern)
  • C++ 单例模式详解
  • 智能决策支持系统的基本概念与理论体系
  • api补充
  • 互联网大厂Java面试:从Spring到微服务的技术探讨
  • 上海市委常委会扩大会议传达学习习近平总书记考察上海重要讲话和在部分省区市“十五五”时期经济社会发展座谈会上的重要讲话精神
  • 科技日报刊文批院士专家“赶场式”跑会:助长浮躁之气功利之心
  • 侯麦:从莫扎特到贝多芬
  • 今年五一档电影票房已破7亿
  • 深入景区、文化街区及消费一线,多地省委书记调研文旅市场
  • 中国企业转口贸易破局之道:出口国多元化,内外贸一体化