Redis的ZSet对象底层原理——跳表

我们来聊聊「跳表(Skip List)」,这是一个既经典又优雅的数据结构,尤其在 Redis 中非常重要,比如 ZSet(有序集合)底层就用到了跳表。


🌟 跳表(Skip List)简介

跳表是一种有序链表的升级版,通过引入多级“索引层”来加快查找效率。

✅ 主要特点:

特性说明
有序结构跳表维护的是一个按 key 排序的链表
多级索引类似高速公路入口,有“快捷路径”
查找时间复杂度O(log n),跟平衡树差不多
实现简单不像红黑树那样复杂
支持范围查找很适合 Redis 中的范围查询场景

📐 跳表结构长啥样?

假设我们有以下数据:[1, 3, 5, 7, 9]

我们构建跳表的过程如下(用 表示链表):

Level 2:      1  →         →       → 9
Level 1:      1  →    5    →       → 9
Level 0:      1  → 3 → 5 → 7 → 9

你可以把它想象成“高架桥+地面道路”的组合:

  • 第 0 层是原始链表,所有节点都在这里
  • 每上一层都是稀疏的抽样索引,跳过一些中间节点,加快查找速度
  • 查找从最上层开始,逐层向下,“跳跃式前进”

🔍 查找过程举例:查找 7

  1. 从最上层的 1 开始 → 跳到 9(9 > 7,不行)
  2. 回退到下一层 → 1 → 5 → 9(9 > 7,继续往下)
  3. 到最底层 → 5 → 7 ✅ 找到!

→ 相比普通链表要顺着一格一格走,跳表“跳着走”,很快!


📈 跳表的时间复杂度

操作时间复杂度
查找O(log n)
插入O(log n)
删除O(log n)
空间复杂度O(n)

相比红黑树、AVL 树,它的代码实现更简单、更易于维护。


💡 跳表的应用场景

场景用途
Redis ZSet支持快速插入、范围查询(如 zrangebyscore)
LSM-Tree(如 RocksDB)早期实现用过跳表
时间序列数据有序插入、范围检索
分布式系统一致性哈希环也可用跳表改进检索速度

🔧 跳表 vs 红黑树

项目跳表红黑树
插入复杂度O(log n)O(log n),但旋转逻辑复杂
实现难度简单复杂(需维持平衡)
查询性能相似相似
空间开销稍高(多指针)较少

🧪 小技巧:跳表的层数怎么选?

跳表是随机化结构,每次插入一个节点时,以一定概率(如 1/2)决定它是否晋升到上一层。

最常见的方式是:

while (random() < p && level < MAX_LEVEL) {level++;
}

所以跳表是一种「概率平衡」的数据结构。


📦 总结

优点缺点
实现简单、性能稳定占用内存较多
支持快速范围查找查询时间有轻微波动
非常适合缓存、有序集合不如 B 树适合磁盘存储

在 Redis 中,跳表(Skip List) 主要用于 Sorted Set(有序集合,zset) 的底层实现。


✅ 使用跳表的场景:zset(有序集合)

Redis 的 zset 类型是一个既可以快速查找元素、又能按照分数排序的数据结构。为了实现这个特性,它使用了 两种结构 来共同实现:

结构作用
哈希表(dict)用于通过成员名快速定位其分数,O(1) 时间复杂度
跳表(skiplist)用于按照分数进行有序排列,支持范围查找、按排名查找等,平均 O(log n) 复杂度

🧠 为什么 Redis 用跳表而不是红黑树?

虽然红黑树、AVL 树等自平衡二叉树也能满足排序要求,但 Redis 选择跳表是因为:

  1. 实现简单、易于维护
  2. 插入和删除操作更平滑,少量随机性避免最坏情况
  3. 天然支持范围查询和按 rank 查找
  4. 跳表是线性结构,遍历更快,利于内存预取(cache friendly)

🧪 举个例子:

127.0.0.1:6379> ZADD myzset 1 a 2 b 3 c
127.0.0.1:6379> ZRANGE myzset 0 -1 WITHSCORES
1) "a"
2) "1"
3) "b"
4) "2"
5) "c"
6) "3"

在内部:

  • Redis 用 dict 维护 a → 1b → 2c → 3
  • 同时用跳表按分数 1 → 2 → 3 排列节点,方便范围查询,比如 ZRANGEBYSCORE

🔧 Redis 跳表结构核心代码(在源码中)

源码路径:/src/t_zset.c
关键结构:zskiplistzskiplistNode

typedef struct zskiplistNode {robj *obj;         // 成员对象double score;      // 分数struct zskiplistNode *backward;struct zskiplistLevel {struct zskiplistNode *forward;unsigned int span;} level[];
} zskiplistNode;

✅ 总结

Redis 中只有 zset 使用跳表,结合 hash dict 实现高效的查找和排序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/78372.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025深圳中兴通讯安卓开发社招面经

2月27号 中兴通讯一面 30多分钟 自我介绍 聊项目 我的优缺点&#xff0c;跟同事相比&#xff0c;有什么突出的地方 Handler机制&#xff0c;如何判断是哪个消息比较耗时 设计模式&#xff1a;模板模式 线程的状态 线程的开启方式 线程池原理 活动的启动模式 Service和Activity…

【Castle-X机器人】二、智能导览模块安装与调试

持续更新。。。。。。。。。。。。。。。 【Castle-X机器人】智能导览模块安装与调试 二、智能导览模块安装与调试2.1 智能导览模块安装2.2 智能导览模块调试2.2.1 红外测温传感器测试2.2.2 2D摄像头测试 二、智能导览模块安装与调试 2.1 智能导览模块安装 使用相应工具将智能…

深入理解二叉树遍历:递归与栈的双重视角

二叉树的遍历前序遍历中序遍历后续遍历总结 二叉树的遍历 虽然用递归的方法遍历二叉树实现起来更简单&#xff0c;但是要想深入理解二叉树的遍历&#xff0c;我们还必须要掌握用栈遍历二叉树&#xff0c;递归其实就是利用了系统栈去遍历。特此记录一下如何用双重视角去看待二叉…

Qt Creator中自定义应用程序的可执行文件图标

要在Qt Creator中为你的应用程序设置自定义可执行文件图标&#xff0c;你需要按照以下步骤操作&#xff1a; Windows平台设置方法 准备图标文件&#xff1a; 创建一个.ico格式的图标文件&#xff08;推荐使用256x256像素&#xff0c;包含多种尺寸&#xff09; 可以使用在线工…

Windows11系统中GIT下载

Windows11系统中GIT下载 0、GIT背景介绍0.0 GIT概述0.1 GIT诞生背景0.2 Linus Torvalds 的设计目标0.3 Git 的诞生&#xff08;2005 年&#xff09;0.4 Git 的后续发展0.5 为什么 Git 能成功&#xff1f; 1、资源下载地址1.1 官网资源1.2 站内资源 2、安装指导3、验证是否下载完…

react的fiber 用法

在 React 里&#xff0c;Fiber 是 React 16.x 及后续版本采用的协调算法&#xff0c;它把渲染工作分割成多个小任务&#xff0c;让 React 可以在渲染过程中暂停、恢复和复用任务&#xff0c;以此提升渲染性能与响应能力。在实际开发中&#xff0c;你无需直接操作 Fiber 节点&am…

FPGA前瞻篇-数字电路基础-逻辑门电路设计

模拟信号&#xff1a; 一条随时间连续变化、平滑波动的曲线&#xff0c;比如正弦波。 数字信号&#xff1a; 一条只有高低两个状态&#xff08;0和1&#xff09;&#xff0c;跳变清晰的方波曲线。 在 IC 或 FPGA 的逻辑设计中&#xff0c;我们通常只能处理数字信号&#xff0…

RabbitMQ 基础概念(队列、交换机、路由键、绑定键、信道、连接、虚拟主机、多租户)介绍

本文是博主在梳理 RabbitMQ 知识的过程中&#xff0c;将所遇到和可能会遇到的基础知识记录下来&#xff0c;用作梳理 RabbitMQ 的整体架构和功能的线索文章&#xff0c;通过查找对应的知识能够快速的了解对应的知识而解决相应的问题。 文章目录 一、RabbitMQ 是什么&#xff1f…

机器学习第一篇 线性回归

数据集&#xff1a;公开的World Happiness Report | Kaggle中的happiness dataset2017. 目标&#xff1a;基于GDP值预测幸福指数。&#xff08;单特征预测&#xff09; 代码&#xff1a; 文件一&#xff1a;prepare_for_traning.py """用于科学计算的一个库…

Java面试高频问题(29-30)

二十九、全链路压测&#xff1a;数据隔离与流量 关键技术点 1. 流量染色&#xff1a;通过Header注入X-Test-TraceId标识压测流量 2. 影子库表&#xff1a;通过ShardingSphere实现数据隔离 3. 熔断降级&#xff1a;压测流量触发异常时自动切换回生产数据源 数据隔离方案对比 …

Python常用的第三方模块之数据分析【pdfplumber库、Numpy库、Pandas库、Matplotlib库】

【pdfplumber库】从PDF文件中读取内容 import pdfplumber #打开PDF文件 with pdfplumber.open(DeepSeek从入门到精通(20250204).pdf) as pdf:for i in pdf.pages: #遍历页print(i.extract_text()) #extract_text()方法提取内容print(f----------------第{i.page_number}页结束…

长短板理论——AI与思维模型【83】

一、定义 长短板理论思维模型&#xff0c;也被称为木桶原理&#xff0c;是指一只木桶能盛多少水&#xff0c;并不取决于最长的那块木板&#xff0c;而是取决于最短的那块木板。该理论将木桶视为一个整体系统&#xff0c;各个木板代表着系统的不同组成部分或要素&#xff0c;强…

2025蓝桥省赛c++B组第二场题解

前言 这场的题目非常的简单啊&#xff0c;至于为什么有第二场&#xff0c;因为当时河北正在刮大风被迫停止了QwQ&#xff0c;个人感觉是历年来最简单的一场&#xff0c;如果有什么不足之处&#xff0c;还望补充。 试题 A: 密密摆放 【问题描述】 小蓝有一个大箱子&#xff0…

【数据结构与算法】从完全二叉树到堆再到优先队列

完全二叉树 CBT 设二叉树的深度为 h , 若非最底层的其他各层的节点数都达到最大个数 , 最底层 h 的所有节点都连续集中在左侧的二叉树叫做 完全二叉树 . 特点 对任意节点 , 其右分支下的叶子节点的最底层为 L , 则其左分支下的叶子节点的最低层一定是 L 或 L 1 .完全二叉树…

Leetcode:1. 两数之和

题目 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案&#xff0c;并且你不能使用两次相同的元素。 你可以按任意顺序返回答案。 示…

flume整合kafka

需求一&#xff1a; 启动flume 启动kafka消费者&#xff0c;验证数据写入成功 新增测试数据 需求二&#xff1a; 启动Kafka生产者 启动Flume 在生产者中写入数据

Hbase集群管理与实践

一、HBase集群搭建实战 1.1 环境规划建议 硬件配置基准(以10节点集群为例): 角色CPU内存磁盘网络HMaster4核16GBSSD 200GB(系统盘)10GbpsRegionServer16核64GB124TB HDD(JBOD)25GbpsZooKeeper4核8GBSSD 500GB10Gbps1.2 关键配置项示例(hbase-site.xml) <configu…

STM32 开发 - stm32f10x.h 头文件(内存映射、寄存器结构体与宏、寄存器位定义、实现点灯案例)

概述 STM32F10x.h 是 STM32F1 系列微控制器的核心头文件&#xff0c;提供了所有外设寄存器的定义和内存映射 一、内存映射 #define PERIPH_BASE ((uint32_t)0x40000000)#define APB1PERIPH_BASE PERIPH_BASE #define APB2PERIPH_BASE (PERIPH_BASE 0x…

QEMU源码全解析 —— 块设备虚拟化(23)

接前一篇文章:QEMU源码全解析 —— 块设备虚拟化(22) 本文内容参考: 《趣谈Linux操作系统》 —— 刘超,极客时间 《QEMU/KVM源码解析与应用》 —— 李强,机械工业出版社 特此致谢! QEMU启动过程中的块设备虚拟化 上一回解析了qcow2格式对应的qcow2_open函数,本回解…

【PCB工艺】推挽电路及交越失真

推挽电路(Push-Pull Circuit) 推挽电路(Push-Pull Circuit) 是一种常用于功率放大、电机驱动、音频放大等场合的电路结构,具有输出对称、效率高、失真小等优点。 什么是推挽电路? 推挽是指:由两种极性相反的器件(如 NPN 和 PNP、NMOS 和 PMOS)交替导通,一个“推”电…