/* Returns a random level for the new skiplist node we are going to create.
* The return value of this function is between 1 and ZSKIPLIST_MAXLEVEL
* (both inclusive), with a powerlaw-alike distribution where higher
* levels are less likely to be returned.
**/intzslRandomLevel(void){// 初始化层级为1
intlevel=1;// 判断随机数的低16位是否小于(ZSKIPLIST_P * 0xFFFF)
while((random()&0xFFFF)<(ZSKIPLIST_P*0xFFFF))// 条件成立,层级 level 增加 1,然后继续判断,直到条件不成立为止。
level+=1;// 返回的层级不超过 ZSKIPLIST_MAXLEVEL,即使循环可能将 level 增加到比最大层级还高的值。
return(level<ZSKIPLIST_MAXLEVEL)?level:ZSKIPLIST_MAXLEVEL;}
/* Create a new skiplist. */zskiplist*zslCreate(void){intj;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;returnzsl;}
新增节点
新增节点的步骤:
找到位置
调整高度
插入节点
调整backward
新建节点:
1
2
3
4
5
6
7
8
/* Create a skiplist node with the specified number of levels.
* The SDS string 'ele' is referenced by the node after the call. */zskiplistNode*zslCreateNode(intlevel,doublescore,sdsele){zskiplistNode*zn=zmalloc(sizeof(*zn)+level*sizeof(structzskiplistLevel));zn->score=score;zn->ele=ele;returnzn;}
zskiplistNode*zslInsert(zskiplist*zsl,doublescore,sdsele){zskiplistNode*update[ZSKIPLIST_MAXLEVEL],*x;unsignedintrank[ZSKIPLIST_MAXLEVEL];inti,level;serverAssert(!isnan(score));x=zsl->header;// 找到位置, 并更新update[] 和rank[]
for(i=zsl->level-1;i>=0;i--){/* store rank that is crossed to reach the insert position */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&&sdscmp(x->level[i].forward->ele,ele)<0))){rank[i]+=x->level[i].span;x=x->level[i].forward;}update[i]=x;}/* we assume the element is not already inside, since we allow duplicated
* scores, reinserting the same element should never happen since the
* caller of zslInsert() should test in the hash table if the element is
* already inside or not. */// 生成新的随机高度,
level=zslRandomLevel();// 判断当前的跳跃表的高度是否小于随机生成的level
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
x=zslCreateNode(level,score,ele);for(i=0;i<level;i++){// 更新x竖向层级上的所有节点的forward属性
x->level[i].forward=update[i]->level[i].forward;update[i]->level[i].forward=x;/* update span covered by update[i] as x is inserted here */x->level[i].span=update[i]->level[i].span-(rank[0]-rank[i]);update[i]->level[i].span=(rank[0]-rank[i])+1;}/* increment span for untouched levels */// 更新
for(i=level;i<zsl->level;i++){update[i]->level[i].span++;}// 更新backward
x->backward=(update[0]==zsl->header)?NULL:update[0];if(x->level[0].forward)x->level[0].forward->backward=x;elsezsl->tail=x;// 增加总长度
zsl->length++;returnx;}
/* Internal function used by zslDelete, zslDeleteByScore and zslDeleteByRank */voidzslDeleteNode(zskiplist*zsl,zskiplistNode*x,zskiplistNode**update){inti;for(i=0;i<zsl->level;i++){if(update[i]->level[i].forward==x){update[i]->level[i].span+=x->level[i].span-1;update[i]->level[i].forward=x->level[i].forward;}else{update[i]->level[i].span-=1;}}if(x->level[0].forward){x->level[0].forward->backward=x->backward;}else{zsl->tail=x->backward;}while(zsl->level>1&&zsl->header->level[zsl->level-1].forward==NULL)zsl->level--;zsl->length--;}
删除跳表
先遍历释放所有节点的内存
再释放跳跃表的内存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* Free a whole skiplist. */voidzslFree(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);}voidzslFreeNode(zskiplistNode*node){sdsfree(node->ele);zfree(node);}
/* Lookup the key and create the sorted set if does not exist. */zobj=lookupKeyWrite(c->db,key);if(zobj==NULL){if(xx)gotoreply_to_client;/* No key + XX option: nothing to do. */if(server.zset_max_ziplist_entries==0||server.zset_max_ziplist_value<sdslen(c->argv[scoreidx+1]->ptr)){zobj=createZsetObject();}else{zobj=createZsetZiplistObject();}dbAdd(c->db,key,zobj);}else{if(zobj->type!=OBJ_ZSET){addReply(c,shared.wrongtypeerr);gotocleanup;}}