1. 跳跃表(skiplist)介绍
定义:跳跃表是一个有序链表,其中每个节点包含不定数量的链接,节点中的第i个链接构成的单向链表跳过含有少于i个链接的节点。
- 跳跃表支持平均O(logN),最坏O(N)
- 复杂度的节点查找,大部分情况下,跳跃表的效率可以和平衡树相媲美。
- 跳跃表在redis中当数据较多时作为有序集合键的实现方式之一。
接下来,还是举个有序集合键的例子:
2. 跳跃表的实现
redis 3.0版本将跳跃表定义在redis.h文件中,而3.2版本定义在server.h文件中
|
|
3. 幂次定律
在redis中,返回一个随机层数值,随机算法所使用的幂次定律。
- 含义是:如果某件事的发生频率和它的某个属性成幂关系,那么这个频率就可以称之为符合幂次定律。
- 表现是:少数几个事件的发生频率占了整个发生频率的大部分, 而其余的大多数事件只占整个发生频率的一个小部分。
在文件t_set.c中,zslRandomLevel函数的定义为:
算法性能分析:
层数至少为1,所以层数恰好等于1(不执行while循环体)的概率为 1−p.
- 层数恰好等于2的概率为 p(1−p)(执行1次while循环体)。
- 层数恰好等于3的概率为 p^2(1−p)(执行2次while循环体)。
- 层数恰好等于4的概率为 p^3(1−p)(执行3次while循环体)。
- 层数恰好等于k的概率为 p^k−1(1−p)(执行k-1次while循环体)。(k <= ZSKIPLIST_MAXLEVEL)
因此,一个节点的平均层数,或平均指针数为:
1×(1−p)+2p(1−p)+3p^2(1−p)+…+kp^(k−1)(1−p)
=1/(1−p)
因此,
当 p = 1/2 时,每个节点的平均指针为2;
当 p = 1/4 时,每个节点的平均指针为1.33;
而redis的概率 ZSKIPLIST_P 取值就为0.25,所以跳跃表的指针开销为1.33
4. 跳跃表与哈希表和平衡树的比较
跳跃表和平衡树的元素都是有序排列,而哈希表不是有序的。因此在哈希表上的查找只能是单个key的查找,不适合做范围查找。
- 跳跃表和平衡树做范围查找时,跳跃表算法简单,实现方便,而平衡树逻辑复杂。
- 查找单个key,跳跃表和平衡树的平均时间复杂度都为O(logN),而哈希表的时间复杂度为O(1)。
- 跳跃表平均每个节点包含1.33个指针,而平衡树每个节点包含2个指针,更加节约内存。
因此,在redis中实现有序集合的办法是:跳跃表+哈希表
- 跳跃表元素有序,而且可以范围查找,且比平衡树简单。
- 哈希表查找单个key时间复杂度性能高。
5 跳跃表基本操作
redis关于跳跃表的API都定义在t_zset.c文件中。
5.1 创建跳跃表 zslCreate()
|
|
5.2 插入节点 zslInsert()
//创建一个节点,分数为score,对象为obj,插入到zsl表头管理的跳跃表中,并返回新节点的地址
4.3 删除节点
//被zslDelete, zslDeleteByScore and zslDeleteByRank使用的内部函数
4.4 获取节点排名
|
|
4.5 区间操作
|
|