Redis 跳表(Skip List)原理实现

 更新时间:2025年04月07日 10:04:10   作者:xiaoyu❅  
跳表是zset有序集合的底层实现之一,本文主要介绍了Redis 跳表(Skip List)原理实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

引言:为什么 Redis 选择跳表?

在有序集合(ZSET)的实现中,Redis 开发者面临一个关键抉择:如何在高性能读写和代码简洁性之间找到平衡?传统平衡树(如红黑树)虽然能保证 O(logN) 时间复杂度,但实现复杂且难以支持范围查询。跳表(Skip List) 以媲美平衡树的性能、极简的实现(约 200 行代码)和天然支持范围查询的特性,成为 Redis ZSET 的核心数据结构。本文将深入剖析跳表的实现细节与 Redis 的工程优化。

一、跳表核心思想:概率化的多层索引

1.1 从链表到跳表的进化

  • 普通链表:插入/删除 O(1),但查询需要 O(N)
  • 跳表创新:通过随机化多层索引,实现对数级查询效率

1.2 跳表结构可视化

Level 3: Head -> 37 --------------------------> 99 -> NULL  
Level 2: Head -> 37 -------> 71 -------> 99 -> NULL  
Level 1: Head -> 37 -> 55 -> 71 -> 85 -> 99 -> NULL  
Level 0: Head -> 37 -> 55 -> 71 -> 85 -> 99 -> NULL  

关键特性

每个节点随机生成层数(Redis 最大层数 64)

高层索引跨越更多节点,加速搜索

底层链表存储完整数据

二、Redis 跳表实现深度解剖

2.1 数据结构定义(redis.h)

// 跳表节点
typedef struct zskiplistNode {
    sds ele;                          // 成员对象(SDS字符串)
    double score;                     // 排序分值
    struct zskiplistNode *backward;   // 后退指针(双向链表)
    struct zskiplistLevel {
        struct zskiplistNode *forward; // 前进指针
        unsigned long span;            // 跨度(用于排名计算)
    } level[];                        // 柔性数组,层级随机生成
} zskiplistNode;

// 跳表结构
typedef struct zskiplist {
    struct zskiplistNode *header, *tail;
    unsigned long length;             // 节点总数
    int level;                        // 当前最大层数
} zskiplist;

设计亮点

  • span 字段:记录节点在某一层的跨度,支持 O(1) 时间复杂度计算元素排名(ZRANK
  • backward 指针:构成双向链表,支持逆序遍历
  • 柔性数组(level[]):内存紧凑,避免指针冗余

2.2 关键操作源码解析

2.2.1 节点层数生成算法

// redis.h 源码节选
int zslRandomLevel(void) {
    int level = 1;
    // 0xFFFF 对应 1/4 概率提升层级(基于位运算优化)
    while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
        level += 1;
    return (level < ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}

数学原理

  • 使用 幂次定律(Power Law),高层节点指数级减少
  • 每个节点有 50% 概率进入 L1,25% 进入 L2,12.5% 进入 L3...
  • Redis 实际使用 1/4 的概率因子(优化内存与性能平衡)

2.2.2 插入节点流程(zslInsert)

  • 搜索路径记录:从最高层开始,记录每层的前驱节点和跨度
  • 生成随机层数:调用 zslRandomLevel
  • 创建新节点:分配层级并连接前后指针
  • 更新跨度:调整相邻节点的 span 值
  • 维护后退指针:设置新节点的 backward 指针

2.2.3 范围查询(ZRANGEBYSCORE)

  • 从高层索引快速定位起点
  • 利用底层链表遍历范围
  • 复杂度:O(logN + M)(M 为返回元素数量)

三、性能分析与优化策略

3.1 时间复杂度对比

操作跳表(平均)跳表(最坏)平衡树
插入O(logN)O(N)O(logN)
删除O(logN)O(N)O(logN)
查找O(logN)O(N)O(logN)
范围查询O(logN + M)O(N)O(logN + M)

:跳表最坏情况(所有节点高度相同)概率极低(例如 1亿节点出现概率为 1/(2^50))

3.2 内存占用分析

  • 理论空间复杂度:O(NlogN)
  • Redis 优化实践:通过 1/4 概率因子,实际空间占用约为 O(1.33N)(实测 100 万节点内存约 64MB)

3.3 调优参数

  • ZSKIPLIST_MAXLEVEL:控制最大层数(默认 64,可调整内存与性能平衡)
  • ZSKIPLIST_P:调整层数生成概率(默认 0.25)

四、跳表在 Redis 中的应用场景

4.1 有序集合(ZSET)

元素数量 > 128 或 元素长度 > 64 字节 时,ZSET 内部使用跳表

支持操作

  • ZADD/ZREM:插入删除
  • ZRANK/ZSCORE:排名查询
  • ZRANGE:范围查询

4.2 集群元数据管理

用于维护槽位(slot)与节点的映射关系

五、跳表 vs 平衡树:工程角度的选择

维度跳表红黑树
实现复杂度约 200 行代码约 500 行代码
范围查询天然支持(链表特性)需要额外遍历
并发控制更易实现无锁优化需要复杂锁机制
调试难度可视化调试友好树旋转逻辑难追踪

Redis 作者 Antirez 的评价:“跳表在理论上不如平衡树优雅,但实际工程中更简单、更快,尤其适合需要范围查询的场景。”

总结:

跳表的精妙之处在于 用概率换结构,通过随机化层级分布避免复杂的再平衡操作。这种“以空间换时间” + “以概率换简单性”的设计哲学,在分布式系统开发中具有重要借鉴意义。理解跳表不仅有助于掌握 Redis 源码,更能启发我们思考如何在高性能与可维护性之间找到平衡。

到此这篇关于Redis 跳表(Skip List)原理实现的文章就介绍到这了,更多相关Redis 跳表内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • redis cluster集群模式下实现批量可重入锁

    redis cluster集群模式下实现批量可重入锁

    本文主要介绍了使用redis cluster集群版所遇到的问题解决方案及redis可重入锁是否会有死锁的问题等,具有一定的参考价值,感兴趣的可以了解一下
    2024-02-02
  • 多维度深入分析Redis的5种基本数据结构

    多维度深入分析Redis的5种基本数据结构

    此篇文章主要对Redis的5种基本数据类型,即字符串(String)、列表(List)、散列(Hash)、集合(Set)、有序集合(Sorted Set),从使用场景和底层结构出发,进行多维度深入分析。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-11-11
  • Redis的4种缓存模式分享

    Redis的4种缓存模式分享

    这篇文章主要介绍了Redis的4种缓存模式分享,文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的小伙伴可以参考一下
    2022-07-07
  • Redis大key和多key拆分的解决方案

    Redis大key和多key拆分的解决方案

    大key会导致内存使用过高,多key可能导致查询效率低下,本文主要介绍了Redis大key和多key拆分的解决方案,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • Linux安装Redis、后台运行、系统自启动的设置方法

    Linux安装Redis、后台运行、系统自启动的设置方法

    Redis是用C语言编写的开源免费的高性能的分布式内存数据库,基于内存运行并支持持久化的NoSQL数据库。这篇文章主要介绍了Linux安装Redis、后台运行、系统自启动,需要的朋友可以参考下
    2020-01-01
  • Redis使用bloom-filter过滤器实现推荐去重

    Redis使用bloom-filter过滤器实现推荐去重

    这篇文章主要介绍了Redis使用bloom-filter过滤器实现推荐去重,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • 利用redis实现排行榜的小秘诀

    利用redis实现排行榜的小秘诀

    这篇文章主要给大家介绍了关于如何利用redis实现排行榜的小秘诀,文中通过示例代码介绍的非常详细,对大家学习或者使用redis具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-03-03
  • Redis和Memcached的区别详解

    Redis和Memcached的区别详解

    这篇文章主要介绍了Redis和Memcached的区别详解,本文从各方面总结了两个数据库的不同之处,需要的朋友可以参考下
    2015-03-03
  • Redis持久化方式之RDB和AOF的原理及优缺点

    Redis持久化方式之RDB和AOF的原理及优缺点

    在Redis中,数据可以分为两类,即内存数据和磁盘数据,Redis 提供了两种不同的持久化方式,其中 RDB 是快照备份机制,AOF 则是追加写操作机制,本文将详细给大家介绍Redis 持久化方式RDB和AOF的原理及优缺点,感兴趣的同学可以跟着小编一起来学习
    2023-06-06
  • redis哨兵模式分布式锁实现与实践方式(redisson)

    redis哨兵模式分布式锁实现与实践方式(redisson)

    这篇文章主要介绍了redis哨兵模式分布式锁实现与实践方式(redisson),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-03-03

最新评论