zset模型和之前讲过的模型都不太一样,别的模型底层有多个单独的实现,来回转换,zset这边是由两个实现来存储的,只不过一种是ziplist,另外一种是hashtable+zskiplist的common zset,其中第二种在
节省空间上面花了心思——共享底层的redis object,其存储结构如下所示
-
typedef struct zset {
-
dict *dict;
-
zskiplist *zsl;
-
} zset;
在数据插入到hashtable的时候,mapping关系是redis object-->score,与此同时,元素插入到skiplist的时候,mapping关系是score--->redis object, 在后一种情况下,元素自然而然的就按照分数排好序了~~~
由于dict当中只有在注册了keyDestructor的时候才会释放空间,zset为了释放元素的时候更加容易管理,这边只是在zslFreeNode()当中释放空间。删除的时候是从dict里rem,后在skiplist中删除。
这里的转换提供了双向转换,从ziplist到common zset和common zset到ziplist的转换都有,前者的转换基本就是没法满足在redis.conf当中设置的两个条件,具体如下:
-
# Similarly to hashes and lists, sorted sets are also specially encoded in
-
# order to save a lot of space. This encoding is only used when the length and
-
# elements of a sorted set are below the following limits:
-
zset-max-ziplist-entries 128
-
zset-max-ziplist-value 64
-
int zsetAdd(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;
-
-
...
-
zobj->ptr = zzlInsert(zobj->ptr,ele,score);
-
if (zzlLength(zobj->ptr) > server.zset_max_ziplist_entries)
-
zsetConvert(zobj,OBJ_ENCODING_SKIPLIST);
-
if (sdslen(ele) > server.zset_max_ziplist_value)
-
zsetConvert(zobj,OBJ_ENCODING_SKIPLIST);
-
if (newscore) *newscore = score;
-
*flags |= ZADD_ADDED;
-
return 1;
-
...
-
return 0; /* Never reached. */
-
}
转换回到ziplist的代码就只有在union操作的时候才用到了~~~
-
void zsetConvertToZiplistIfNeeded(robj *zobj, size_t maxelelen) {
-
if (zobj->encoding == OBJ_ENCODING_ZIPLIST) return;
-
zset *zset = zobj->ptr;
-
-
if (zset->zsl->length <= server.zset_max_ziplist_entries &&
-
maxelelen <= server.zset_max_ziplist_value)
-
zsetConvert(zobj,OBJ_ENCODING_ZIPLIST);
-
}
-
void zunionInterGenericCommand(client *c, robj *dstkey, int op) {
-
...
-
-
/* parse optional extra arguments */
-
...
-
/* sort sets from the smallest to largest, this will improve our
-
* algorithm's performance */
-
...
-
/* Step 1: Create a dictionary of elements -> aggregated-scores
-
* by iterating one sorted set after the other. */
-
...
-
/* Step 2: convert the dictionary into the final sorted set. */
-
...
-
if (dbDelete(c->db,dstkey))
-
touched = 1;
-
if (dstzset->zsl->length) {
-
zsetConvertToZiplistIfNeeded(dstobj,maxelelen);
-
dbAdd(c->db,dstkey,dstobj);
-
addReplyLongLong(c,zsetLength(dstobj));
-
signalModifiedKey(c->db,dstkey);
-
notifyKeyspaceEvent(NOTIFY_ZSET,
-
(op == SET_OP_UNION) ? "zunionstore" : "zinterstore",
-
dstkey,c->db->id);
-
server.dirty++;
-
} else {
-
decrRefCount(dstobj);
-
addReply(c,shared.czero);
-
if (touched) {
-
signalModifiedKey(c->db,dstkey);
-
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",dstkey,c->db->id);
-
server.dirty++;
-
}
-
}
-
zfree(src);
-
}
真正执行转换的代码如下:
-
void zsetConvert(robj *zobj, int encoding) {
-
zset *zs;
-
zskiplistNode *node, *next;
-
sds ele;
-
double score;
-
-
if (zobj->encoding == encoding) return;
-
if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {
-
unsigned char *zl = zobj->ptr;
-
unsigned char *eptr, *sptr;
-
unsigned char *vstr;
-
unsigned int vlen;
-
long long vlong;
-
-
if (encoding != OBJ_ENCODING_SKIPLIST)
-
serverPanic("Unknown target encoding");
-
-
zs = zmalloc(sizeof(*zs));
-
zs->dict = dictCreate(&zsetDictType,NULL);
-
zs->zsl = zslCreate();
-
-
eptr = ziplistIndex(zl,0);
-
serverAssertWithInfo(NULL,zobj,eptr != NULL);
-
sptr = ziplistNext(zl,eptr);
-
serverAssertWithInfo(NULL,zobj,sptr != NULL);
-
-
while (eptr != NULL) {
-
score = zzlGetScore(sptr);
-
serverAssertWithInfo(NULL,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));
-
if (vstr == NULL)
-
ele = sdsfromlonglong(vlong);
-
else
-
ele = sdsnewlen((char*)vstr,vlen);
-
-
node = zslInsert(zs->zsl,score,ele);
-
serverAssert(dictAdd(zs->dict,ele,&node->score) == DICT_OK);
-
zzlNext(zl,&eptr,&sptr);
-
}
-
-
zfree(zobj->ptr);
-
zobj->ptr = zs;
-
zobj->encoding = OBJ_ENCODING_SKIPLIST;
-
} else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {
-
unsigned char *zl = ziplistNew();
-
-
if (encoding != OBJ_ENCODING_ZIPLIST)
-
serverPanic("Unknown target encoding");
-
-
/* Approach similar to zslFree(), since we want to free the skiplist at
-
* the same time as creating the ziplist. */
-
zs = zobj->ptr;
-
dictRelease(zs->dict);
-
node = zs->zsl->header->level[0].forward;
-
zfree(zs->zsl->header);
-
zfree(zs->zsl);
-
-
while (node) {
-
zl = zzlInsertAt(zl,NULL,node->ele,node->score);
-
next = node->level[0].forward;
-
zslFreeNode(node);
-
node = next;
-
}
-
-
zfree(zs);
-
zobj->ptr = zl;
-
zobj->encoding = OBJ_ENCODING_ZIPLIST;
-
} else {
-
serverPanic("Unknown sorted set encoding");
-
}
-
}
给外界的接口套路都差不多:先找到数据库,在数据库里对指定的存储操作,操作结束后发送事件通知,这里只列出了rem的代码,因为其他的实在是太长了- -|||,见谅见谅:
-
void zremCommand(client *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,OBJ_ZSET)) return;
-
-
for (j = 2; j < c->argc; j++) {
-
if (zsetDel(zobj,c->argv[j]->ptr)) deleted++;
-
if (zsetLength(zobj) == 0) {
-
dbDelete(c->db,key);
-
keyremoved = 1;
-
break;
-
}
-
}
-
-
if (deleted) {
-
notifyKeyspaceEvent(NOTIFY_ZSET,"zrem",key,c->db->id);
-
if (keyremoved)
-
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",key,c->db->id);
-
signalModifiedKey(c->db,key);
-
server.dirty += deleted;
-
}
-
addReplyLongLong(c,deleted);
-
}
还请各位多提宝贵意见~~~
阅读(3281) | 评论(0) | 转发(0) |