Redis版本:7.0.2
入口-saddCommand函数
sadd命令对应函数saddCommand,源码如下:
c
void saddCommand(client *c) {
robj *set;
int j, added = 0;
set = lookupKeyWrite(c->db,c->argv[1]);
if (checkType(c,set,OBJ_SET)) return;
if (set == NULL) {
set = setTypeCreate(c->argv[2]->ptr);
dbAdd(c->db,c->argv[1],set);
}
for (j = 2; j < c->argc; j++) {
if (setTypeAdd(set,c->argv[j]->ptr)) added++;
}
if (added) {
signalModifiedKey(c,c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_SET,"sadd",c->argv[1],c->db->id);
}
server.dirty += added;
addReplyLongLong(c,added);
}
逻辑如下:
- 通过lookupKeyWrite函数查找key,获取值为set。
- 如果set为NULL,则调用setTypeCreate函数创建一个set对象,然后调用dbAdd函数将key添加到全局字典中。
- 遍历参数列表,对每个参数调用setTypeAdd函数添加到set中。
- 如果添加了元素,则调用signalModifiedKey函数发送信号,调用notifyKeyspaceEvent函数发送事件通知,更新server.dirty(变更次数统计)。
- 最后调用addReplyLongLong函数返回添加的元素个数。
初始化对象
c
/* Factory method to return a set that *can* hold "value". When the object has
* an integer-encodable value, an intset will be returned. Otherwise a regular
* hash table.
* 返回一个set的工厂方法。如果对象有一个整数可编码的值,将返回一个intset(整数集合)。否则返回一个普通的哈希表。
* */
robj *setTypeCreate(sds value) {
if (isSdsRepresentableAsLongLong(value,NULL) == C_OK)
return createIntsetObject();
return createSetObject();
}
/*****************************object.c*********************************/
robj *createIntsetObject(void) {
intset *is = intsetNew();
robj *o = createObject(OBJ_SET,is);
o->encoding = OBJ_ENCODING_INTSET;
return o;
}
robj *createSetObject(void) {
dict *d = dictCreate(&setDictType);
robj *o = createObject(OBJ_SET,d);
o->encoding = OBJ_ENCODING_HT;
return o;
}
逻辑为:
- 如果值可以转换为long long,则调用createIntsetObject,创建一个intset类型对象。
- 否则,调用createSetObject,创建一个dict类型对象。
添加元素-setTypeAdd
setTypeAdd函数位于t_set.c中,源码如下:
c
/*******************************t_set.c************************************/
/* Add the specified value into a set.
* 向集合中添加特定值。
*
* If the value was already member of the set, nothing is done and 0 is
* returned, otherwise the new element is added and 1 is returned.
* 如果值已经是集合的成员,则不执行任何操作并返回0,否则添加新元素并返回1。
* */
int setTypeAdd(robj *subject, sds value) {
long long llval;
if (subject->encoding == OBJ_ENCODING_HT) {
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.
* 当intset包含太多条目时,转换为常规集合(意思是转换为字典类型)。
*/
size_t max_entries = server.set_max_intset_entries;
/* limit to 1G entries due to intset internals.
* 由于intset内部限制,限制为1G条目。
*/
if (max_entries >= 1<<30) max_entries = 1<<30;
if (intsetLen(subject->ptr) > max_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;
}
逻辑为:
- 判断subject的编码类型,如果是dict类型,则调用dictAddRaw函数添加元素。
- 添加成功后,调用dictSetKey和dictSetVal函数设置key和val。key为要添加的值,val为NULL。
- 如果是initset类型,则判断值是否可以转换为整数,则调用intsetAdd函数添加元素。
整数集合 中最多有1G个键。
- 如果值不可以转换为整数,则调用setTypeConvert函数将集合对象转换为dict类型,然后调用dictAdd函数添加元素。