Redis实现-跳跃表 一、什么是跳跃表 跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。
跳跃表支持平均O(logN)、最坏O(N)复杂度
。
在大部分情况下,跳跃表的效率可以和平衡树相媲美,并且因为跳跃表的实现比平衡树要更简单。
1 2 3 4 5 1. 一个跳跃表应该有若干个层(Level)链表组成2. 跳跃表中最底层的链表包含所有数据; 每一层链表中的数据都是有序的3. 如果一个元素 X 出现在第i层,那么编号比 i 小的层都包含元素 X4. 第 i 层的元素通过一个指针指向下一层拥有相同值的元素5. 头指针(head)指向最高一层的第一个元素
二、跳跃表在Redis中的使用 Redis使用跳跃表作为有序集合键的底层实现之一,如果一个有序集合包含的元素数量比较多,或者有序集合中的元素的成员是比较长的字符串时,Redis就会使用跳跃表来作为有序集合键的底层实现。
1 2 3 4 5 6 7 8 127.0.0.1:6379> ZADD runoobket 1 redis (integer) 1 127.0.0.1:6379> ZADD runoobket 2 mongodb (integer) 1 127.0.0.1:6379> ZADD runoobket 3 mysql (integer) 1 127.0.0.1:6379> ZADD runoobket 4 mysql (integer) 0
三、跳跃表的实现 在Redis中,跳跃表有两个结构来实现。zskiplistNode和zskiplist
。
zskiplist
结构用于保存跳跃表节点的相关信息。
zskiplistNode
用于表示跳跃表的节点。
1、跳跃表节点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 typedef struct zskiplistNode { robj *obj; double score; struct zskiplistNode *backward ; struct zskiplistLevel { struct zskiplistNode *forward ; unsigned int span; } level[]; } zskiplistNode;
层
节点中的层是一个level[]数组。包含一个指向其他节点的指针。
上图中展示了节点分别为1层和3层。
前进指针
每一层都有一个指向表尾的前进指针(level[n].forward属性)
,用于从表头向表尾方法访问节点,实际就是这个前进指针就是遍历跳跃表的所有节点的路径。
跨度
层的跨度(level[n].span属性)
用于记录两个节点之间的距离。
1 2 1. 两个节点之间的跨度越大,它们相距就越远2. 前进指针是NULL时,跨度为0,因为它们没有连向任何节点
跨度实际上是用来计算排位(rank)的:在查找某个节点的过程中,将沿途访问过的所有层的跨度累计起来,得到的结果就是目标节点在跳跃表的排位。
后退指针
节点中的后退指针(backword属性)
用于从表尾向表头方向访问节点。
每个节点有多层,就有多个前进指针。但一个节点只有后退指针,而且每次只能后退到当前节点的前一个结点。
分值
节点的分值(score属性)
是一个double 类型的浮点数,跳跃表中的所有结点都按分值来排序。
成员
节点的成员(obj属性)
是一个指针,指向一个字符串对象,这个字符串对象保存一个SDS值。
在同一个跳跃表中,各个节点保存的成员对象必须是唯一的。但是节点的分值可以是相同的。
如果分值相同,那么节点将按照成员对象在字典序中的大小来进行排序。
2、跳跃表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 typedef struct zskiplist { struct zskiplistNode *header , *tail ; unsigned long length; int level; } zskiplist;
多个跳跃表节点通过前进指针和后退指针进行连接。
我们通过zskiplist
结构来持有这些节点。
header和tail指针分别指向跳跃表的表头和表尾节点,通过这两个节点,程序可以快速定位表头节点和表尾节点,时间复杂度为o(1)
。
length属性来记录节点的数量,表头结点不计算在内。
level属性,记录最大的层数。
四、源码 创建一个层数为 level 的跳跃表节点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 zskiplistNode *zslCreateNode (int level, double score, robj *obj) { zskiplistNode *zn = zmalloc(sizeof (*zn)+level*sizeof (struct zskiplistLevel)); zn->score = score; zn->obj = obj; return zn; }
创建并返回一个新的跳跃表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 zskiplist *zslCreate (void ) { int j; zskiplist *zsl; zsl = zmalloc(sizeof (*zsl)); zsl->level = 1 ; zsl->length = 0 ; zsl->header = zslCreateNode(ZSKIPLIST_MAXLEVEL,0 ,NULL ); for (j = 0 ; j < ZSKIPLIST_MAXLEVEL; j++) { zsl->header->level[j].forward = NULL ; zsl->header->level[j].span = 0 ; } zsl->header->backward = NULL ; zsl->tail = NULL ; return zsl; }
将新节点插入到跳跃表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 zskiplistNode *zslInsert (zskiplist *zsl, double score, robj *obj) { zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x; unsigned int rank[ZSKIPLIST_MAXLEVEL]; int i, level; redisAssert(!isnan(score)); x = zsl->header; for (i = zsl->level-1 ; i >= 0 ; i--) { rank[i] = i == (zsl->level-1 ) ? 0 : rank[i+1 ]; while (x->level[i].forward && (x->level[i].forward->score < score || (x->level[i].forward->score == score && compareStringObjects(x->level[i].forward->obj,obj) < 0 ))) { rank[i] += x->level[i].span; x = x->level[i].forward; } update[i] = x; } level = zslRandomLevel(); if (level > zsl->level) { for (i = zsl->level; i < level; i++) { rank[i] = 0 ; update[i] = zsl->header; update[i]->level[i].span = zsl->length; } zsl->level = level; } x = zslCreateNode(level,score,obj); for (i = 0 ; i < level; i++) { x->level[i].forward = update[i]->level[i].forward; update[i]->level[i].forward = x; x->level[i].span = update[i]->level[i].span - (rank[0 ] - rank[i]); update[i]->level[i].span = (rank[0 ] - rank[i]) + 1 ; } for (i = level; i < zsl->level; i++) { update[i]->level[i].span++; } x->backward = (update[0 ] == zsl->header) ? NULL : update[0 ]; if (x->level[0 ].forward) x->level[0 ].forward->backward = x; else zsl->tail = x; zsl->length++; return x; }
从跳跃表删除给定节点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 int zslDelete (zskiplist *zsl, double score, robj *obj) { zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x; int i; x = zsl->header; for (i = zsl->level-1 ; i >= 0 ; i--) { while (x->level[i].forward && (x->level[i].forward->score < score || (x->level[i].forward->score == score && compareStringObjects(x->level[i].forward->obj,obj) < 0 ))) x = x->level[i].forward; update[i] = x; } x = x->level[0 ].forward; if (x && score == x->score && equalStringObjects(x->obj,obj)) { zslDeleteNode(zsl, x, update); zslFreeNode(x); return 1 ; } else { return 0 ; } return 0 ; }
释放给定跳跃表,以及表中的所有节点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void zslFree (zskiplist *zsl) { zskiplistNode *node = zsl->header->level[0 ].forward, *next; zfree(zsl->header); while (node) { next = node->level[0 ].forward; zslFreeNode(node); node = next; } zfree(zsl); }