***********************************************************************************************
typedef struct zskiplistNode {
sds ele; 元素
double score; 分值
struct zskiplistNode *backward; 后向指针,用于层次1
struct zskiplistLevel { 每层
struct zskiplistNode *forward; 向前的指针
unsigned long span; 跳跃的间隔
} level[];
} zskiplistNode;
/* Create a skiplist node with the specified number of levels.
* The SDS string 'ele' is referenced by the node after the call. */
创建一个带有确定层级的跳表节点,通过调用,SDS字符串'ele'被节点引用
zskiplistNode *zslCreateNode(int level, double score, sds ele) {
zskiplistNode *zn =
zmalloc(sizeof(*zn)+level*sizeof(struct zskiplistLevel)); 节点本身的空间 和 层数 * 每层所需空间
zn->score = score; 节点的数值
zn->ele = ele; 节点的元素
return zn;
}
***********************************************************************************************
typedef struct zskiplist {
struct zskiplistNode *header, *tail; 头和尾指针
unsigned long length; 跳表长度
int level; 所在层次
} zskiplist;
/* Create a new skiplist. */
zskiplist *zslCreate(void) {
int j;
zskiplist *zsl;
zsl = zmalloc(sizeof(*zsl)); 创建跳表所需初始空间
zsl->level = 1; 初始只有1层
zsl->length = 0; 长度为0
zsl->header = zslCreateNode(ZSKIPLIST_MAXLEVEL,0,NULL); 创建头节点,层级最高为32,数值为0,不带元素
//#define ZSKIPLIST_MAXLEVEL 32 /* Should be enough for 2^64 elements */
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;
}
***********************************************************************************************
/* Free the specified skiplist node. The referenced SDS string representation
* of the element is freed too, unless node->ele is set to NULL before calling
* this function. */
释放指定的跳表节点。元素引用的SDS字符串也需要释放,除非节点的元素在调用本函数前已经被置为NULL
void zslFreeNode(zskiplistNode *node) {
sdsfree(node->ele);
zfree(node);
}
***********************************************************************************************
/* Free a whole skiplist. */ 释放整个跳表(先释放跳表内部申请的节点空间,再释放跳表本身)
void zslFree(zskiplist *zsl) {
zskiplistNode *node = zsl->header->level[0].forward, *next;
zfree(zsl->header); 释放头节点
while(node) { 节点不为空
next = node->level[0].forward; 通过第一层的指向关系获取下一个节点(因为任何节点都有第一层即level[0])
zslFreeNode(node); 把当前节点释放掉
node = next; 将下个节点置成当前节点
}
zfree(zsl); 释放跳表本身
}
***********************************************************************************************
/* 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. */
为将要新建的跳表节点返回一个随机的层次。
本函数返回的值介于1到ZSKIPLIST_MAXLEVEL之间(包括头和尾),即[1,ZSKIPLIST_MAXLEVEL]
拥有一个类似幂律分布,较高层级不太可能被返回(层数越高返回的可能性越小)
int zslRandomLevel(void) {
int level = 1;
//#define ZSKIPLIST_P 0.25 /* Skiplist P = 1/4 */
while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF)) 当随机值小0.25的时候,层级就上升一层
level += 1;
return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
如果层级没有超过定义的最高层,就返回本身,否则返回最高层的数值
}
***********************************************************************************************
/* Insert a new node in the skiplist. Assumes the element does not already
* exist (up to the caller to enforce that). The skiplist takes ownership
* of the passed SDS string 'ele'. */
在跳跃链表插入一个新节点。假设这个元素不存在(直到调用者执行)。
跳表获得传入SDS字符串ele的拥有权。
zskiplistNode *zslInsert(zskiplist *zsl, double score, sds ele) {
zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
unsigned int rank[ZSKIPLIST_MAXLEVEL]; 保留跳跃的步数
int i, level;
serverAssert(!isnan(score)); 确认传入的数值是合理数值
x = zsl->header; 指向头部
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]; 初始化,最高层初始值为0,下层初始值为上层的结束值
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. */
我们假设元素不在跳表内部,因为我们允许重复的数值,重复插入同样的元素不应该发生,
所以函数zslInsert的调用者需要在哈希表中测试元素是否已经在内部或者没有
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,ele); 创建新的节点
for (i = 0; i < level; i++) { 对新节点每层进行信息插入
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插入时,更新对应层级的跳跃步数
x->level[i].span = update[i]->level[i].span - (rank[0] - rank[i]);
新节点x层级i对应的跳步 = 原节点层级i对应的跳步 - (新节点插入的最终位置-i层原节点所在位置)
如下图所示
--------停止位置------------------------------------------------------------------------------rank[level-1]
...
----------------------------停止位置-------------------------|----------原指向的节点位置------rank[i]
... |<----rank[0] - rank[i]----->| |
--------------------------------|---------------------停止位置(最终位置)--------|-------------rank[0]
|
--------------------------------|<----------原节点层级i对应的跳步-------------->|----------------
那么rank[0] - rank[i] 代表 最终位置 和 对应i层 之间的距离
update[i]->level[i].span - (rank[0] - rank[i]) 就代表新节点到原节点指向的前节点的距离
update[i]->level[i].span = (rank[0] - rank[i]) + 1; 加1就是要把新节点也计算在内
}
/* increment span for untouched levels */
新增节点没有使用的层,原来所在位置的节点的跳跃步数都需要增加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++; 跳表总长度增加1
return x; 返回这个新增节点
}
***********************************************************************************************
/* Internal function used by zslDelete, zslDeleteRangeByScore and
* zslDeleteRangeByRank. */
被函数zslDelete, zslDeleteRangeByScore 和 zslDeleteRangeByRank使用的内部函数, 用来删除节点
void zslDeleteNode(zskiplist *zsl, zskiplistNode *x, zskiplistNode **update) {
int i;
for (i = 0; i < zsl->level; i++) {
if (update[i]->level[i].forward == x) { 如果当前节点的前向节点是x,x就是将要被删除的节点
update[i]->level[i].span += x->level[i].span - 1;
那么这个节点的跳跃步数 需要加上其前向节点(就是x)的跳跃步数,同时减去这个删除节点
update[i]->level[i].forward = x->level[i].forward; 把删除节点的前向节点赋值给 当前节点的前向节点
} else {
update[i]->level[i].span -= 1;不是前向节点,那么对应的跳跃步数减去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
}
***********************************************************************************************
/* Delete an element with matching score/element from the skiplist.
* The function returns 1 if the node was found and deleted, otherwise
* 0 is returned.
从跳表中删除匹配数值或者元素的节点。节点被找到和删除就返回1,否则返回0
* If 'node' is NULL the deleted node is freed by zslFreeNode(), otherwise
* it is not freed (but just unlinked) and *node is set to the node pointer,
* so that it is possible for the caller to reuse the node (including the
* referenced SDS string at node->ele). */
如果传入是空,通过函数zslFreeNode释放删除节点, 否则不释放(只是不链在表中),设置*node指向这个删除节点。
因此调用者重新使用这个节点变得可能(包括在节点node->ele中引用的SDS字符串)
int zslDelete(zskiplist *zsl, double score, sds ele, zskiplistNode **node) {
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 && 或者数值虽然相等,但是具体的字符串比小于传入的字符串,也需要继续向前
sdscmp(x->level[i].forward->ele,ele) < 0)))
{
x = x->level[i].forward;
}
update[i] = x; 记录本层停止的位置
}
/* We may have multiple elements with the same score, what we need
* is to find the element with both the right score and object. */
我们拥有很多同样数值的元素,我们需要找出那个具有同样数值和对象内容的元素.(数值和字符串才构成唯一)
x = x->level[0].forward; 第一层停止的位置,就是呀删除的位置(如果存在)
if (x && score == x->score && sdscmp(x->ele,ele) == 0) {
zslDeleteNode(zsl, x, update);
if (!node) 传入空,释放这个节点
zslFreeNode(x);
else
*node = x; 非空的情况下,通过指针node返回删除节点
return 1;
}
return 0; /* not found */
}
***********************************************************************************************
/* Update the score of an elmenent inside the sorted set skiplist.
* Note that the element must exist and must match 'score'.
* This function does not update the score in the hash table side, the
* caller should take care of it.
更新一个在排序结合跳表中的元素的数值。注意这个元素必须存在而且必须匹配这个数值。
这个函数不更新hash表中的数值,调用者需要注意这点。
* Note that this function attempts to just update the node, in case after
* the score update, the node would be exactly at the same position.
* Otherwise the skiplist is modified by removing and re-adding a new
* element, which is more costly.
注意如果经过数值调整,这个节点可能还是在原来的位置,那么这个函数仅仅只需要更新节点,
否则需要通过重新移除和重新添加一个新节点,这种情况代价比较高。
* The function returns the updated element skiplist node pointer. */
本函数返回指向更新后的跳表中的节点
zskiplistNode *zslUpdateScore(zskiplist *zsl, double curscore, sds ele, double newscore) {
zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
int i;
/* We need to seek to element to update to start: this is useful anyway,
* we'll have to update or remove it. */
我们需要寻找更新元素的开始: 这个很有用,我们必须更新或删除它
( 这个部分同插入和删除的前奏一样,确定层的停止位置。确实很很有用!)
x = zsl->header;
for (i = zsl->level-1; i >= 0; i--) {
while (x->level[i].forward &&
(x->level[i].forward->score < curscore ||
(x->level[i].forward->score == curscore &&
sdscmp(x->level[i].forward->ele,ele) < 0)))
{
x = x->level[i].forward;
}
update[i] = x;
}
/* Jump to our element: note that this function assumes that the
* element with the matching score exists. */
调到我们需要处理的元素:注意这个函数假设匹配数值的元素存在
x = x->level[0].forward;
serverAssert(x && curscore == x->score && sdscmp(x->ele,ele) == 0); 假设了存在,所以检测下,万一不存在就是程序出问题了
/* If the node, after the score update, would be still exactly
* at the same position, we can just update the score without
* actually removing and re-inserting the element in the skiplist. */
如果节点经过数值更新,还是保持原位,我们值需要更新数值即可,不需要将元素移除和重新插入跳表。
if ((x->backward == NULL || x->backward->score < newscore) && 本节点前一个节点的数值 小于 本节点新的数值
(x->level[0].forward == NULL || x->level[0].forward->score > newscore)) 本节点后一个节点的数值 大于 本节点新的数值
{
x->score = newscore; 只需要更新数值即可
return x;
}
/* No way to reuse the old node: we need to remove and insert a new
* one at a different place. */
不能使用老节点:我们需要移除(老节点)和在不同的地方插入一个新节点
zslDeleteNode(zsl, x, update); 将老节点删除
zskiplistNode *newnode = zslInsert(zsl,newscore,x->ele); 插入新节点
/* We reused the old node x->ele SDS string, free the node now
* since zslInsert created a new one. */
我们重用了老节点的sds字符串,因为函数zslInsert创建了一个新节点,所以我们可以释放老节点了。(sds字符串有新节点管了,不会有内存泄漏)
x->ele = NULL;
zslFreeNode(x);
return newnode;
}
***********************************************************************************************