/* Create a skiplist node with the specified number of levels. * The SDS string 'ele' is referenced by the node after the call. */ 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; }
/* 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'. */ zskiplistNode *zslInsert(zskiplist *zsl, double score, sds ele){ zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x; unsignedint 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]; 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. */ //返回1-maxlevel的随机数 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->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++; }
/* Add a new element or update the score of an existing element in a sorted * set, regardless of its encoding. * * The set of flags change the command behavior. They are passed with an integer * pointer since the function will clear the flags and populate them with * other flags to indicate different conditions. * * The input flags are the following: * * ZADD_INCR: Increment the current element score by 'score' instead of updating * the current element score. If the element does not exist, we * assume 0 as previous score. * ZADD_NX: Perform the operation only if the element does not exist. * ZADD_XX: Perform the operation only if the element already exist. * * When ZADD_INCR is used, the new score of the element is stored in * '*newscore' if 'newscore' is not NULL. * * The returned flags are the following: * * ZADD_NAN: The resulting score is not a number. * ZADD_ADDED: The element was added (not present before the call). * ZADD_UPDATED: The element score was updated. * ZADD_NOP: No operation was performed because of NX or XX. * * Return value: * * The function returns 1 on success, and sets the appropriate flags * ADDED or UPDATED to signal what happened during the operation (note that * none could be set if we re-added an element using the same score it used * to have, or in the case a zero increment is used). * * The function returns 0 on erorr, currently only when the increment * produces a NAN condition, or when the 'score' value is NAN since the * start. * * The commad as a side effect of adding a new element may convert the sorted * set internal encoding from ziplist to hashtable+skiplist. * * Memory managemnet of 'ele': * * The function does not take ownership of the 'ele' SDS string, but copies * it if needed. */ intzsetAdd(robj *zobj, double score, sds ele, int *flags, double *newscore){ /* Turn options into simple to check vars. */ int incr = (*flags & ZADD_INCR) != 0; int nx = (*flags & ZADD_NX) != 0; int xx = (*flags & ZADD_XX) != 0; *flags = 0; /* We'll return our response flags. */ double curscore;
/* NaN as input is an error regardless of all the other parameters. */ if (isnan(score)) { *flags = ZADD_NAN; return0; }
/* Update the sorted set according to its encoding. */ //压缩列表,暂时不看 if (zobj->encoding == OBJ_ENCODING_ZIPLIST) { unsignedchar *eptr;
if ((eptr = zzlFind(zobj->ptr,ele,&curscore)) != NULL) { /* NX? Return, same element already exists. */ if (nx) { *flags |= ZADD_NOP; return1; }
/* Prepare the score for the increment if needed. */ if (incr) { score += curscore; if (isnan(score)) { *flags |= ZADD_NAN; return0; } if (newscore) *newscore = score; }
/* Remove and re-insert when score changed. */ if (score != curscore) { zobj->ptr = zzlDelete(zobj->ptr,eptr); zobj->ptr = zzlInsert(zobj->ptr,ele,score); *flags |= ZADD_UPDATED; } return1; } elseif (!xx) { /* Optimize: check if the element is too large or the list * becomes too long *before* executing zzlInsert. */ zobj->ptr = zzlInsert(zobj->ptr,ele,score); if (zzlLength(zobj->ptr) > server.zset_max_ziplist_entries || sdslen(ele) > server.zset_max_ziplist_value) zsetConvert(zobj,OBJ_ENCODING_SKIPLIST); if (newscore) *newscore = score; *flags |= ZADD_ADDED; return1; } else { *flags |= ZADD_NOP; return1; } //跳表 } elseif (zobj->encoding == OBJ_ENCODING_SKIPLIST) { zset *zs = zobj->ptr; zskiplistNode *znode; dictEntry *de;
//先从hash找,这里相当于是hash+skip list的结合,hash在其他post另外整理 de = dictFind(zs->dict,ele); //ele原来就存在 if (de != NULL) { /* NX? Return, same element already exists. */ //nx是只新增,不更新 所以就ret if (nx) { *flags |= ZADD_NOP; return1; } curscore = *(double*)dictGetVal(de);
/* Prepare the score for the increment if needed. */ if (incr) { score += curscore; if (isnan(score)) { *flags |= ZADD_NAN; return0; } if (newscore) *newscore = score; }
/* Remove and re-insert when score changes. */ //更新分数 if (score != curscore) { //更新跳表的分值 znode = zslUpdateScore(zs->zsl,curscore,ele,score); /* Note that we did not removed the original element from * the hash table representing the sorted set, so we just * update the score. */ //更新hash的分值 dictGetVal(de) = &znode->score; /* Update score ptr. */ *flags |= ZADD_UPDATED; } return1; //插入的是新的元素 xx是只更新不新增,所以这里过滤下 } elseif (!xx) { ele = sdsdup(ele); //插入 znode = zslInsert(zs->zsl,score,ele); serverAssert(dictAdd(zs->dict,ele,&znode->score) == DICT_OK); *flags |= ZADD_ADDED; if (newscore) *newscore = score; return1; } else { *flags |= ZADD_NOP; return1; } } else { serverPanic("Unknown sorted set encoding"); } return0; /* Never reached. */ }
/* 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. * * 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=current的位置 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. */ //case1 x->backward == NULL && x->level[0].forward == NULL //只有一个节点直接更新
/* 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. */ x->ele = NULL; zslFreeNode(x); return newnode; }
/* 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. * * 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). */ intzslDelete(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; return1; } return0; /* not found */ }