redis当中集合对象的底层实现为intset和hashtable实现,用hashtable实现时,存储具体值的是key,value统一用NULL。其实集合对象的实现和hash对象的实现还是非常类似的,都是尽可能用占用空间小的底层类型存储,如果实在存不下了,就得鸟枪换炮了
老规矩,还是先说转换的条件,由于占地较小的实现为intset,这就导致发生转化的条件比
zipmap->hashtable要不一样了,但也是一共两项,若有一项或一项以上没法满足,则intset转为hashtable:
1. 集合对象均为整数值;
2. intset中的元素个数超过512个
其中第二个限制可以在redis.conf文件中修改
-
# set in order to use this special memory saving encoding.
-
set-max-intset-entries 512
其转换代码如下:
-
void setTypeConvert(robj *setobj, int enc) {
-
setTypeIterator *si;
-
serverAssertWithInfo(NULL,setobj,setobj->type == OBJ_SET &&
-
setobj->encoding == OBJ_ENCODING_INTSET);
-
-
if (enc == OBJ_ENCODING_HT) {
-
int64_t intele;
-
dict *d = dictCreate(&setDictType,NULL);
-
sds element;
-
-
/* Presize the dict to avoid rehashing */
-
dictExpand(d,intsetLen(setobj->ptr));
-
-
/* To add the elements we extract integers and create redis objects */
-
si = setTypeInitIterator(setobj);
-
while (setTypeNext(si,&element,&intele) != -1) {
-
element = sdsfromlonglong(intele);
-
serverAssert(dictAdd(d,element,NULL) == DICT_OK);
-
}
-
setTypeReleaseIterator(si);
-
-
setobj->encoding = OBJ_ENCODING_HT;
-
zfree(setobj->ptr);
-
setobj->ptr = d;
-
} else {
-
serverPanic("Unsupported set conversion");
-
}
-
}
其中用到转换代码的函数如下所示:
-
int setTypeAdd(robj *subject, sds value) {
-
long long llval;
-
if (subject->encoding == OBJ_ENCODING_HT) {/*如果是hashtable,直接加*/
-
dict *ht = subject->ptr;
-
dictEntry *de = dictAddRaw(ht,value,NULL);
-
if (de) {
-
dictSetKey(ht,de,sdsdup(value));
-
dictSetVal(ht,de,NULL);
-
return 1;
-
}
-
} else if (subject->encoding == OBJ_ENCODING_INTSET) {
-
if (isSdsRepresentableAsLongLong(value,&llval) == C_OK) {//先判断是否为整数*/
-
uint8_t success = 0;
-
subject->ptr = intsetAdd(subject->ptr,llval,&success);/*插入是否成功*/
-
if (success) {
-
/* Convert to regular set when the intset contains
-
* too many entries. */
-
if (intsetLen(subject->ptr) > server.set_max_intset_entries)/*超过阈值*/
-
setTypeConvert(subject,OBJ_ENCODING_HT);
-
return 1;
-
}
-
} else {/*转换类型*/
-
/* Failed to get integer from object, convert to regular set. */
-
setTypeConvert(subject,OBJ_ENCODING_HT);
-
-
/* The set *was* an intset and this value is not integer
-
* encodable, so dictAdd should always work. */
-
serverAssert(dictAdd(subject->ptr,sdsdup(value),NULL) == DICT_OK);
-
return 1;
-
}
-
} else {
-
serverPanic("Unknown set encoding");
-
}
-
return 0;
-
}
至于其留给客户端的命令接口,这里也只列出一个,其余不再赘述:
-
void saddCommand(client *c) {
-
robj *set;
-
int j, added = 0;
-
-
set = lookupKeyWrite(c->db,c->argv[1]);/*在db中查找指定key*/
-
if (set == NULL) {/*为空则创建*/
-
set = setTypeCreate(c->argv[2]->ptr);
-
dbAdd(c->db,c->argv[1],set);
-
} else {/*不为空则插入*/
-
if (set->type != OBJ_SET) {/*判断*/
-
addReply(c,shared.wrongtypeerr);
-
return;
-
}
-
}
-
-
for (j = 2; j < c->argc; j++) {
-
if (setTypeAdd(set,c->argv[j]->ptr)) added++;
-
}
-
if (added) {
-
signalModifiedKey(c->db,c->argv[1]);
-
notifyKeyspaceEvent(NOTIFY_SET,"sadd",c->argv[1],c->db->id);
-
}
-
server.dirty += added;
-
addReplyLongLong(c,added);
-
}
不足之处还请大家多多指正~~~
阅读(6839) | 评论(0) | 转发(0) |