当前位置:首页 > 问答 > 正文

Redis跳表到底能有多高层数,实际最多能堆叠几层呢?

关于Redis跳表层数的具体问题,我们首先要看它的设计规则,根据Redis的官方源代码和相关的设计文档(比如Redis的创作者Salvatore Sanfilippo在博客和邮件列表中的说明),Redis里的跳表并不是一个固定层数的结构,它的层数是动态的,并且带有一点随机性。

Redis中的每一个要插入跳表的元素,都会被分配一个随机的层数,这个层数是怎么决定的呢?它用的是类似抛硬币的原理,一个新节点首先肯定会有第1层,也就是最底层,程序就开始“抛硬币”,如果结果是“正面”(在程序里就是随机数满足某个条件),那么这个节点就获得第2层,接着再抛一次硬币,如果又是“正面”,它就获得第3层,就这样一直重复“抛硬币”的过程,直到某一次抛出“反面”为止,这个节点的层数也就确定了,一个节点有可能只有1层,也有可能运气非常好,连续抛出多次“正面”,从而获得很高的层数。

这个“抛硬币”的概率是怎么设定的呢?在Redis的源码中(具体在server.h文件里关于zskiplistNode的定义部分),这个概率被设定为四分之一,也就是25%的机会能向上一层,这意味着,大约每4个节点里,有1个会有第2层;每16个节点里,有1个会有第3层;每64个节点里,有1个会有第4层……以此类推,层数越高,拥有该层数的节点就越稀少。

Redis跳表到底能有多高层数,实际最多能堆叠几层呢?

现在回到核心问题:它到底能有多高?实际最多能堆叠几层?从理论上讲,因为这是一个随机过程,只要随机数生成器足够“给力”,理论上层数是可以非常非常高的,甚至达到成百上千层,Redis在实现时,为了避免在极端罕见的情况下出现过于夸张、浪费内存的层数,它设置了一个硬性的上限,在Redis的源代码中(具体在server.h文件中),明确定义了一个常量叫做ZSKIPLIST_MAXLEVEL,这个常量的值被设置为32,这意味着,无论一个节点的“运气”多么好,它在Redis的跳表里最多也只能拥有32层。Redis跳表的实际最大层数是32层

为什么要设置这样一个上限呢?这主要是出于工程上的考虑,是一种权衡,如果允许层数无限增长,虽然在极其偶然的情况下能创造出搜索路径极短的“超级节点”,但更大概率是会导致内存的浪费,因为维护高层数的指针是需要额外空间的,设置一个合理的上限,比如32层,对于Redis实际应用的场景已经完全足够了,根据概率计算,当层数上限为32时,即使跳表中存储了海量的元素(比如数十亿甚至更多个),最顶层的节点数量也会非常少,但仍然能保证跳表高效的搜索效率,其查找时间复杂度接近O(log N),这个32层的上限,被实践证明是在内存使用和搜索性能之间一个非常好的平衡点。

Redis跳表到底能有多高层数,实际最多能堆叠几层呢?

我们可以做一个简单的计算来感受一下:按照25%的概率向上,拥有第32层的节点,其出现的概率是(1/4)^31,这是一个极其微小的数字,意味着可能需要存储天文数字般的节点,才有可能出现一个达到32层的节点,在绝大多数实际应用中,跳表的实际高度可能远远达不到32层,可能只有十几层或二十层左右,但这个32层的“天花板”确保了即使在最极端的情况下,数据结构的行为也是可控和高效的。

Redis跳表的层数是通过一种“抛硬币”的随机算法动态生成的,这保证了跳表结构的平衡性不需要复杂的调整就能维持,而为了避免理论上的无限高层数可能带来的问题,Redis源码中设定了一个实际的最大层数限制,即32层,这个设计是Redis追求简单性和高性能的典型体现。

(主要设计思想和参数来源参考自Redis开源项目源码中的server.ht_zset.c文件,以及Redis创始人Salvatore Sanfilippo的相关技术解说)