0.前言
Redis有序集合ZSet可以按分数进行排序, 存储结构可能使用ziplist,skiplist和hash表, zset_max_ziplist_entries和zset_max_ziplist_value两个字段控制zset采用何种存储方式, zset_max_ziplist_entries表示ziplist中存储score和member占用的内存空间超过该值, 则存储结构会转变为skiplist和hash表; zset_max_ziplist_value表示ziplist中存储的member值占用的内存空间超过该值, 则存储结构会转变为skiplist和hash表. 存储使用ziplist时, ziplist存储格式为[member, score, member, score....], 以score值升序进行排序.存储使用skiplist时, 需要hash表配合使用, hash表存储以member为key, score为值, 加快member检索score速度; skiplist存储score和member, 并以score值进行升序排序.
1.目录
2.ZADD命令
添加元素到有序集合中, 命令格式 : ZADD key score member [[score member] [score member] ...], 入口函数zaddCommand
void zaddCommand(redisClient *c) {
zaddGenericCommand(c,0);
}
/*函数向有序集合中添加一个元素, 在incr值设置时, 同时可以实现对score值进行累加操作*/
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;
if (c->argc % 2) {
addReply(c,shared.syntaxerr);
return;
}
/* 获取scores值, 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;
}
/* 如果有序集合不存在, 直接进行创建 */
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))
{
/*创建有序集合, 存储结构式skiplist*/
zobj = createZsetObject();
} else {
/*创建有序集合, 存储结构式ziplist*/
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];
if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *eptr;
/* 在skiplist中进行查找, 找到则删除原来的, 插入新的, 否则直接进行插入操作*/
ele = c->argv[3+j*2];
if ((eptr = zzlFind(zobj->ptr,ele,&curscore)) != NULL) {
/*incr值设置, 则需要进行累加*/
if (incr) {
score += curscore;
if (isnan(score)) {
addReplyError(c,nanerr);
goto cleanup;
}
}
/* 如果member和score都没有变化, 则不进行任何操作*/
if (score != curscore) {
zobj->ptr = zzlDelete(zobj->ptr,eptr);
zobj->ptr = zzlInsert(zobj->ptr,ele,score);
server.dirty++;
updated++;
}
} else {
/* 同样插入元素时进行检测ziplist转skiplist的阀值*/
zobj->ptr = zzlInsert(zobj->ptr,ele,score);
if (zzlLength(zobj->ptr) > server.zset_max_ziplist_entries)
zsetConvert(zobj,REDIS_ENCODING_SKIPLIST);
if (sdslen(ele->ptr) > server.zset_max_ziplist_value)
zsetConvert(zobj,REDIS_ENCODING_SKIPLIST);
server.dirty++;
added++;
}
} else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {
zset *zs = zobj->ptr;
zskiplistNode *znode;
dictEntry *de;
/*存储结构为skiplist时, 首先从hash表中通过member查找到score, 同样找到删除原来的, 找不到则直接插入*/
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);
if (incr) {
score += curscore;
if (isnan(score)) {
addReplyError(c,nanerr);
goto cleanup;
}
}
/* member和score完全一样, 则不进行任何操作*/
if (score != curscore) {
redisAssert