基础的zset相关的命令,t_zset.c

zadd——zaddCommand

示例:ZADD KEY_NAME SCORE1 VALUE1.. SCOREN VALUEN

  • 被成功添加的新成员的数量,不包括那些被更新的、已经存在的成员。
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
void zaddCommand(redisClient *c) {
zaddGenericCommand(c,0);
}


/* This generic command implements both ZADD and ZINCRBY. */
void zaddGenericCommand(redisClient *c, int incr) {
static char *nanerr = "resulting score is not a number (NaN)";
robj *key = c->argv[1];
robj *ele;
robj *zobj;
robj *curobj;
double score = 0, *scores = NULL, curscore = 0.0;
int j, elements = (c->argc-2)/2;
int added = 0, updated = 0;
// 输入的 score - member 参数必须是成对出现的
if (c->argc % 2) {
addReply(c,shared.syntaxerr);
return;
}
/* Start parsing all the scores, we need to emit any syntax error
* before executing additions to the sorted set, as the command should
* either execute fully or nothing at all. */
// 取出所有输入的 score 分值
scores = zmalloc(sizeof(double)*elements);
for (j = 0; j < elements; j++) {
if (getDoubleFromObjectOrReply(c,c->argv[2+j*2],&scores[j],NULL)
!= REDIS_OK) goto cleanup;
}
/* Lookup the key and create the sorted set if does not exist. */
// 取出有序集合对象
zobj = lookupKeyWrite(c->db,key);
if (zobj == NULL) {
// 有序集合不存在,创建新有序集合
if (server.zset_max_ziplist_entries == 0 ||
server.zset_max_ziplist_value < sdslen(c->argv[3]->ptr))
{
zobj = createZsetObject();
} else {
zobj = createZsetZiplistObject();
}
// 关联对象到数据库
dbAdd(c->db,key,zobj);
} else {
// 对象存在,检查类型
if (zobj->type != REDIS_ZSET) {
addReply(c,shared.wrongtypeerr);
goto cleanup;
}
}
// 处理所有元素
for (j = 0; j < elements; j++) {
score = scores[j];
// 有序集合为 ziplist 编码
if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *eptr;
/* Prefer non-encoded element when dealing with ziplists. */
// 查找成员
ele = c->argv[3+j*2];
if ((eptr = zzlFind(zobj->ptr,ele,&curscore)) != NULL) {
// 成员已存在
// ZINCRYBY 命令时使用
if (incr) {
score += curscore;
if (isnan(score)) {
addReplyError(c,nanerr);
goto cleanup;
}
}
/* Remove and re-insert when score changed. */
// 执行 ZINCRYBY 命令时,
// 或者用户通过 ZADD 修改成员的分值时执行
if (score != curscore) {
// 删除已有元素
zobj->ptr = zzlDelete(zobj->ptr,eptr);
// 重新插入元素
zobj->ptr = zzlInsert(zobj->ptr,ele,score);
// 计数器
server.dirty++;
updated++;
}
} else {
/* Optimize: check if the element is too large or the list
* becomes too long *before* executing zzlInsert. */
// 元素不存在,直接添加
zobj->ptr = zzlInsert(zobj->ptr,ele,score);
// 查看元素的数量,
// 看是否需要将 ZIPLIST 编码转换为有序集合
if (zzlLength(zobj->ptr) > server.zset_max_ziplist_entries)
zsetConvert(zobj,REDIS_ENCODING_SKIPLIST);
// 查看新添加元素的长度
// 看是否需要将 ZIPLIST 编码转换为有序集合
if (sdslen(ele->ptr) > server.zset_max_ziplist_value)
zsetConvert(zobj,REDIS_ENCODING_SKIPLIST);
server.dirty++;
added++;
}
// 有序集合为 SKIPLIST 编码
} else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {
zset *zs = zobj->ptr;
zskiplistNode *znode;
dictEntry *de;
// 编码对象
ele = c->argv[3+j*2] = tryObjectEncoding(c->argv[3+j*2]);
// 查看成员是否存在
de = dictFind(zs->dict,ele);
if (de != NULL) {
// 成员存在
// 取出成员
curobj = dictGetKey(de);
// 取出分值
curscore = *(double*)dictGetVal(de);
// ZINCRYBY 时执行
if (incr) {
score += curscore;
if (isnan(score)) {
addReplyError(c,nanerr);
/* Don't need to check if the sorted set is empty
* because we know it has at least one element. */
goto cleanup;
}
}
/* Remove and re-insert when score changed. We can safely
* delete the key object from the skiplist, since the
* dictionary still has a reference to it. */
// 执行 ZINCRYBY 命令时,
// 或者用户通过 ZADD 修改成员的分值时执行
if (score != curscore) {
// 删除原有元素
redisAssertWithInfo(c,curobj,zslDelete(zs->zsl,curscore,curobj));
// 重新插入元素
znode = zslInsert(zs->zsl,score,curobj);
incrRefCount(curobj); /* Re-inserted in skiplist. */
// 更新字典的分值指针
dictGetVal(de) = &znode->score; /* Update score ptr. */
server.dirty++;
updated++;
}
} else {
// 元素不存在,直接添加到跳跃表
znode = zslInsert(zs->zsl,score,ele);
incrRefCount(ele); /* Inserted in skiplist. */
// 将元素关联到字典
redisAssertWithInfo(c,NULL,dictAdd(zs->dict,ele,&znode->score) == DICT_OK);
incrRefCount(ele); /* Added to dictionary. */
server.dirty++;
added++;
}
} else {
redisPanic("Unknown sorted set encoding");
}
}
if (incr) /* ZINCRBY */
addReplyDouble(c,score);
else /* ZADD */
addReplyLongLong(c,added);
cleanup:
zfree(scores);
if (added || updated) {
signalModifiedKey(c->db,key);
notifyKeyspaceEvent(REDIS_NOTIFY_ZSET,
incr ? "zincr" : "zadd", key, c->db->id);
}
}

zincrby——zincrbyCommand

示例:ZINCRBY key increment member

  • member 成员的新分数值,以字符串形式表示。
1
2
3
void zincrbyCommand(redisClient *c) {
zaddGenericCommand(c,1);
}

zrem——zremCommand

示例:ZREM key member [member …]

  • 被成功移除的成员的数量,不包括被忽略的成员。
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
void zremCommand(redisClient *c) {
robj *key = c->argv[1];
robj *zobj;
int deleted = 0, keyremoved = 0, j;
// 取出有序集合对象
if ((zobj = lookupKeyWriteOrReply(c,key,shared.czero)) == NULL ||
checkType(c,zobj,REDIS_ZSET)) return;
// 从 ziplist 中删除
if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *eptr;
// 遍历所有输入元素
for (j = 2; j < c->argc; j++) {
// 如果元素在 ziplist 中存在的话
if ((eptr = zzlFind(zobj->ptr,c->argv[j],NULL)) != NULL) {
// 元素存在时,删除计算器才增一
deleted++;
// 那么删除它们
zobj->ptr = zzlDelete(zobj->ptr,eptr);

// ziplist 已清空,将有序集合从数据库中删除
if (zzlLength(zobj->ptr) == 0) {
dbDelete(c->db,key);
break;
}
}
}
// 从跳跃表和字典中删除
} else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {
zset *zs = zobj->ptr;
dictEntry *de;
double score;
// 遍历所有输入元素
for (j = 2; j < c->argc; j++) {
// 查找元素
de = dictFind(zs->dict,c->argv[j]);
if (de != NULL) {
// 元素存在时,删除计算器才增一
deleted++;
/* Delete from the skiplist */
// 将元素从跳跃表中删除
score = *(double*)dictGetVal(de);
redisAssertWithInfo(c,c->argv[j],zslDelete(zs->zsl,score,c->argv[j]));
/* Delete from the hash table */
// 将元素从字典中删除
dictDelete(zs->dict,c->argv[j]);
// 检查是否需要缩小字典
if (htNeedsResize(zs->dict)) dictResize(zs->dict);
// 字典已被清空,有序集合已经被清空,将它从数据库中删除
if (dictSize(zs->dict) == 0) {
dbDelete(c->db,key);
break;
}
}
}
} else {
redisPanic("Unknown sorted set encoding");
}
// 如果有至少一个元素被删除的话,那么执行以下代码
if (deleted) {
notifyKeyspaceEvent(REDIS_NOTIFY_ZSET,"zrem",key,c->db->id);
if (keyremoved)
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",key,c->db->id);
signalModifiedKey(c->db,key);
server.dirty += deleted;
}
// 回复被删除元素的数量
addReplyLongLong(c,deleted);
}

zremrangebyscore——zremrangebyscoreCommand

示例:ZREMRANGEBYSCORE key min max

  • 被移除成员的数量。
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
void zremrangebyscoreCommand(redisClient *c) {
zremrangeGenericCommand(c,ZRANGE_SCORE);
}
/* Implements ZREMRANGEBYRANK, ZREMRANGEBYSCORE, ZREMRANGEBYLEX commands. */
#define ZRANGE_RANK 0
#define ZRANGE_SCORE 1
#define ZRANGE_LEX 2
void zremrangeGenericCommand(redisClient *c, int rangetype) {
robj *key = c->argv[1];
robj *zobj;
int keyremoved = 0;
unsigned long deleted;
zrangespec range;
zlexrangespec lexrange;
long start, end, llen;
/* Step 1: Parse the range. */
if (rangetype == ZRANGE_RANK) {
if ((getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != REDIS_OK) ||
(getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK))
return;
} else if (rangetype == ZRANGE_SCORE) {
if (zslParseRange(c->argv[2],c->argv[3],&range) != REDIS_OK) {
addReplyError(c,"min or max is not a float");
return;
}
} else if (rangetype == ZRANGE_LEX) {
if (zslParseLexRange(c->argv[2],c->argv[3],&lexrange) != REDIS_OK) {
addReplyError(c,"min or max not valid string range item");
return;
}
}
/* Step 2: Lookup & range sanity checks if needed. */
if ((zobj = lookupKeyWriteOrReply(c,key,shared.czero)) == NULL ||
checkType(c,zobj,REDIS_ZSET)) goto cleanup;
if (rangetype == ZRANGE_RANK) {
/* Sanitize indexes. */
llen = zsetLength(zobj);
if (start < 0) start = llen+start;
if (end < 0) end = llen+end;
if (start < 0) start = 0;
/* Invariant: start >= 0, so this test will be true when end < 0.
* The range is empty when start > end or start >= length. */
if (start > end || start >= llen) {
addReply(c,shared.czero);
goto cleanup;
}
if (end >= llen) end = llen-1;
}
/* Step 3: Perform the range deletion operation. */
if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {
switch(rangetype) {
case ZRANGE_RANK:
zobj->ptr = zzlDeleteRangeByRank(zobj->ptr,start+1,end+1,&deleted);
break;
case ZRANGE_SCORE:
zobj->ptr = zzlDeleteRangeByScore(zobj->ptr,&range,&deleted);
break;
case ZRANGE_LEX:
zobj->ptr = zzlDeleteRangeByLex(zobj->ptr,&lexrange,&deleted);
break;
}
if (zzlLength(zobj->ptr) == 0) {
dbDelete(c->db,key);
keyremoved = 1;
}
// 从跳跃表和字典中删除
} else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {
zset *zs = zobj->ptr;
switch(rangetype) {
case ZRANGE_RANK:
deleted = zslDeleteRangeByRank(zs->zsl,start+1,end+1,zs->dict);
break;
case ZRANGE_SCORE:
deleted = zslDeleteRangeByScore(zs->zsl,&range,zs->dict);
break;
case ZRANGE_LEX:
deleted = zslDeleteRangeByLex(zs->zsl,&lexrange,zs->dict);
break;
}
if (htNeedsResize(zs->dict)) dictResize(zs->dict);
// 对象已清空,从数据库中删除
if (dictSize(zs->dict) == 0) {
dbDelete(c->db,key);
keyremoved = 1;
}
} else {
redisPanic("Unknown sorted set encoding");
}
/* Step 4: Notifications and reply. */
if (deleted) {
char *event[3] = {"zremrangebyrank","zremrangebyscore","zremrangebylex"};
signalModifiedKey(c->db,key);
notifyKeyspaceEvent(REDIS_NOTIFY_ZSET,event[rangetype],key,c->db->id);
if (keyremoved)
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",key,c->db->id);
}
server.dirty += deleted;
// 回复被删除元素的个数
addReplyLongLong(c,deleted);
cleanup:
if (rangetype == ZRANGE_LEX) zslFreeLexRange(&lexrange);
}


/*
* 删除 ziplist 中分值在指定范围内的元素
*
* deleted 不为 NULL 时,在删除完毕之后,将被删除元素的数量保存到 *deleted 中。
*/
unsigned char *zzlDeleteRangeByScore(unsigned char *zl, zrangespec *range, unsigned long *deleted) {
unsigned char *eptr, *sptr;
double score;
unsigned long num = 0;
if (deleted != NULL) *deleted = 0;
// 指向 ziplist 中第一个符合范围的节点
eptr = zzlFirstInRange(zl,range);
if (eptr == NULL) return zl;
/* When the tail of the ziplist is deleted, eptr will point to the sentinel
* byte and ziplistNext will return NULL. */
// 一直删除节点,直到遇到不在范围内的值为止
// 节点中的值都是有序的
while ((sptr = ziplistNext(zl,eptr)) != NULL) {
score = zzlGetScore(sptr);
if (zslValueLteMax(score,range)) {
/* Delete both the element and the score. */
zl = ziplistDelete(zl,&eptr);
zl = ziplistDelete(zl,&eptr);
num++;
} else {
/* No longer in range. */
break;
}
}
if (deleted != NULL) *deleted = num;
return zl;
}
/* Delete all the elements with score between min and max from the skiplist.
*
* 删除所有分值在给定范围之内的节点。
*
* Min and max are inclusive, so a score >= min || score <= max is deleted.
*
* min 和 max 参数都是包含在范围之内的,所以分值 >= min 或 <= max 的节点都会被删除。
*
* Note that this function takes the reference to the hash table view of the
* sorted set, in order to remove the elements from the hash table too.
*
* 节点不仅会从跳跃表中删除,而且会从相应的字典中删除。
*
* 返回值为被删除节点的数量
*
* T = O(N)
*/
unsigned long zslDeleteRangeByScore(zskiplist *zsl, zrangespec *range, dict *dict) {
zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
unsigned long removed = 0;
int i;
// 记录所有和被删除节点(们)有关的节点
// T_wrost = O(N) , T_avg = O(log N)
x = zsl->header;
for (i = zsl->level-1; i >= 0; i--) {
while (x->level[i].forward && (range->minex ?
x->level[i].forward->score <= range->min :
x->level[i].forward->score < range->min))
x = x->level[i].forward;
update[i] = x;
}
/* Current node is the last with score < or <= min. */
// 定位到给定范围开始的第一个节点
x = x->level[0].forward;
/* Delete nodes while in range. */
// 删除范围中的所有节点
// T = O(N)
while (x &&
(range->maxex ? x->score < range->max : x->score <= range->max))
{
// 记录下个节点的指针
zskiplistNode *next = x->level[0].forward;
zslDeleteNode(zsl,x,update);
dictDelete(dict,x->obj);
zslFreeNode(x);
removed++;
x = next;
}
return removed;
}

zremrangebyrank——zremrangebyrankCommand

示例:ZREMRANGEBYSCORE key min max

  • 被移除成员的数量。
1
2
3
void zremrangebyrankCommand(client *c) {
zremrangeGenericCommand(c,ZRANGE_RANK);
}
命令 作用 ziplist编码时间复杂度 skiplist编码时间复杂度
命令 作用 ziplist编码时间复杂度 skiplist编码时间复杂度
ZADD 将一个或多个元素加入到指定有序集合当中 平均O(N), 最坏O(N2) 平均O(logN), 最坏O(N)
ZCARD 返回指定有序集合的元素数量 O(1) O(1)
ZCOUNT 返回有序集合中,score 值在 min 和 max 之间成员的数量 O(n) O(log(n))
ZRANGE 返回有序集合中,指定区间内的成员 O(n) O(log(n)+m),n为有序集合的长度,m为结果集的长度
ZREVRANGE 返回有序集 key 中,指定区间内的成员,其中有序集成员按 score 值递减(从大到小)顺序排列 O(n) O(log(n)+m),n为有序集合的长度,m为结果集的长度
ZRANK 返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递增(从小到大)顺序排列 O(n) O(log(n))
ZREVRANK 返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递减(从大到小)排序 O(n) O(log(n))
ZREM 移除有序集 key 中的一个或多个成员,不存在的成员将被忽略 平均O(N), 最坏O(N2) O(m*log(n)),m为需要移除的元素数量,n为有序中的元素数量
ZSCORE 返回有序集 key 中,成员 member 的 score 值 O(n) O(1)