setTypeCreate:返回一个集合对象
robj *setTypeCreate(robj *value) {
if (isObjectRepresentableAsLongLong(value,NULL) == REDIS_OK)
return createIntsetObject();
return createSetObject();
}
setTypeAdd:向集合中添加元素
int setTypeAdd(robj *subject, robj *value) {
long long llval;
// 字典
if (subject->encoding == REDIS_ENCODING_HT) {
// 将 value 作为键, NULL 作为值,将元素添加到字典中
if (dictAdd(subject->ptr,value,NULL) == DICT_OK) {
incrRefCount(value);
return 1;
}
// intset
} else if (subject->encoding == REDIS_ENCODING_INTSET) {
// 如果对象的值可以编码为整数的话,那么将对象的值添加到 intset 中
if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_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,REDIS_ENCODING_HT);
return 1;
}
// 如果对象的值不能编码为整数,那么将集合从 intset 编码转换为 HT 编码
// 然后再执行添加操作
} else {
/* Failed to get integer from object, convert to regular set. */
setTypeConvert(subject,REDIS_ENCODING_HT);
/* The set *was* an intset and this value is not integer
* encodable, so dictAdd should always work. */
redisAssertWithInfo(NULL,value,dictAdd(subject->ptr,value,NULL) == DICT_OK);
incrRefCount(value);
return 1;
}
// 未知编码
} else {
redisPanic("Unknown set encoding");
}
// 添加失败,元素已经存在
return 0;
}
setTypeRemove:删除某个操作
int setTypeRemove(robj *setobj, robj *value) {
long long llval;
// HT
if (setobj->encoding == REDIS_ENCODING_HT) {
// 从字典中删除键(集合元素)
if (dictDelete(setobj->ptr,value) == DICT_OK) {
// 看是否有必要在删除之后缩小字典的大小
if (htNeedsResize(setobj->ptr)) dictResize(setobj->ptr);
return 1;
}
// INTSET
} else if (setobj->encoding == REDIS_ENCODING_INTSET) {
// 如果对象的值可以编码为整数的话,那么尝试从 intset 中移除元素
if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) {
int success;
setobj->ptr = intsetRemove(setobj->ptr,llval,&success);
if (success) return 1;
}
// 未知编码
} else {
redisPanic("Unknown set encoding");
}
// 删除失败
return 0;
}
setTypeIsMember:检查是否有某个元素
int setTypeIsMember(robj *subject, robj *value) {
long long llval;
// HT
if (subject->encoding == REDIS_ENCODING_HT) {
return dictFind((dict*)subject->ptr,value) != NULL;
// INTSET
} else if (subject->encoding == REDIS_ENCODING_INTSET) {
if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) {
return intsetFind((intset*)subject->ptr,llval);
}
// 未知编码
} else {
redisPanic("Unknown set encoding");
}
// 查找失败
return 0;
}
setTypeRandomElement:从集合中随机获取一个元素
int setTypeRandomElement(robj *setobj, robj **objele, int64_t *llele) {
if (setobj->encoding == REDIS_ENCODING_HT) {
dictEntry *de = dictGetRandomKey(setobj->ptr);
*objele = dictGetKey(de);
} else if (setobj->encoding == REDIS_ENCODING_INTSET) {
*llele = intsetRandom(setobj->ptr);
} else {
redisPanic("Unknown set encoding");
}
return setobj->encoding;
}
setTypeSize:返回集合中元素的个数
unsigned long setTypeSize(robj *subject) {
if (subject->encoding == REDIS_ENCODING_HT) {
return dictSize((dict*)subject->ptr);
} else if (subject->encoding == REDIS_ENCODING_INTSET) {
return intsetLen((intset*)subject->ptr);
} else {
redisPanic("Unknown set encoding");
}
}
saddCommand:sadd命令
void saddCommand(redisClient *c) {
robj *set;
int j, added = 0;
// 取出集合对象
set = lookupKeyWrite(c->db,c->argv[1]);
// 对象不存在,创建一个新的,并将它关联到数据库
if (set == NULL) {
set = setTypeCreate(c->argv[2]);
dbAdd(c->db,c->argv[1],set);
// 对象存在,检查类型
} else {
if (set->type != REDIS_SET) {
addReply(c,shared.wrongtypeerr);
return;
}
}
// 将所有输入元素添加到集合中
for (j = 2; j < c->argc; j++) {
c->argv[j] = tryObjectEncoding(c->argv[j]);
// 只有元素未存在于集合时,才算一次成功添加
if (setTypeAdd(set,c->argv[j])) added++;
}
// 如果有至少一个元素被成功添加,那么执行以下程序
if (added) {
// 发送键修改信号
signalModifiedKey(c->db,c->argv[1]);
// 发送事件通知
notifyKeyspaceEvent(REDIS_NOTIFY_SET,"sadd",c->argv[1],c->db->id);
}
// 将数据库设为脏
server.dirty += added;
// 返回添加元素的数量
addReplyLongLong(c,added);
}
sremCommand:删除多个值
void sremCommand(redisClient *c) {
robj *set;
int j, deleted = 0, keyremoved = 0;
// 取出集合对象
if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,set,REDIS_SET)) return;
// 删除输入的所有元素
for (j = 2; j < c->argc; j++) {
// 只有元素在集合中时,才算一次成功删除
if (setTypeRemove(set,c->argv[j])) {
deleted++;
// 如果集合已经为空,那么删除集合对象
if (setTypeSize(set) == 0) {
dbDelete(c->db,c->argv[1]);
keyremoved = 1;
break;
}
}
}
// 如果有至少一个元素被成功删除,那么执行以下程序
if (deleted) {
// 发送键修改信号
signalModifiedKey(c->db,c->argv[1]);
// 发送事件通知
notifyKeyspaceEvent(REDIS_NOTIFY_SET,"srem",c->argv[1],c->db->id);
// 发送事件通知
if (keyremoved)
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",c->argv[1],
c->db->id);
// 将数据库设为脏
server.dirty += deleted;
}
// 将被成功删除元素的数量作为回复
addReplyLongLong(c,deleted);
}
smoveCommand:将指定元素从源集合移动到目的集合
void smoveCommand(redisClient *c) {
robj *srcset, *dstset, *ele;
// 取出源集合
srcset = lookupKeyWrite(c->db,c->argv[1]);
// 取出目标集合
dstset = lookupKeyWrite(c->db,c->argv[2]);
// 编码元素
ele = c->argv[3] = tryObjectEncoding(c->argv[3]);
/* If the source key does not exist return 0 */
// 源集合不存在,直接返回
if (srcset == NULL) {
addReply(c,shared.czero);
return;
}
/* If the source key has the wrong type, or the destination key
* is set and has the wrong type, return with an error. */
// 如果源集合的类型错误
// 或者目标集合存在、但是类型错误
// 那么直接返回
if (checkType(c,srcset,REDIS_SET) ||
(dstset && checkType(c,dstset,REDIS_SET))) return;
/* If srcset and dstset are equal, SMOVE is a no-op */
// 如果源集合和目标集合相等,那么直接返回
if (srcset == dstset) {
addReply(c,shared.cone);
return;
}
/* If the element cannot be removed from the src set, return 0. */
// 从源集合中移除目标元素
// 如果目标元素不存在于源集合中,那么直接返回
if (!setTypeRemove(srcset,ele)) {
addReply(c,shared.czero);
return;
}
// 发送事件通知
notifyKeyspaceEvent(REDIS_NOTIFY_SET,"srem",c->argv[1],c->db->id);
/* Remove the src set from the database when empty */
// 如果源集合已经为空,那么将它从数据库中删除
if (setTypeSize(srcset) == 0) {
// 删除集合对象
dbDelete(c->db,c->argv[1]);
// 发送事件通知
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",c->argv[1],c->db->id);
}
// 发送键修改信号
signalModifiedKey(c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[2]);
// 将数据库设为脏
server.dirty++;
/* Create the destination set when it doesn't exist */
// 如果目标集合不存在,那么创建它
if (!dstset) {
dstset = setTypeCreate(ele);
dbAdd(c->db,c->argv[2],dstset);
}
/* An extra key has changed when ele was successfully added to dstset */
// 将元素添加到目标集合
if (setTypeAdd(dstset,ele)) {
// 将数据库设为脏
server.dirty++;
// 发送事件通知
notifyKeyspaceEvent(REDIS_NOTIFY_SET,"sadd",c->argv[2],c->db->id);
}
// 回复 1
addReply(c,shared.cone);
}
sismemberCommand:检查是否存在指定成员
void sismemberCommand(redisClient *c) {
robj *set;
// 取出集合对象
if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,set,REDIS_SET)) return;
// 编码输入元素
c->argv[2] = tryObjectEncoding(c->argv[2]);
// 检查是否存在
if (setTypeIsMember(set,c->argv[2]))
addReply(c,shared.cone);
else
addReply(c,shared.czero);
}
scardCommand:获取成员的集合数目
void scardCommand(redisClient *c) {
robj *o;
// 取出集合
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,o,REDIS_SET)) return;
// 返回集合的基数
addReplyLongLong(c,setTypeSize(o));
}
spopCommand:随机弹出一个集合中的元素
void spopCommand(redisClient *c) {
robj *set, *ele, *aux;
int64_t llele;
int encoding;
// 取出集合
if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
checkType(c,set,REDIS_SET)) return;
// 从集合中随机取出一个元素
encoding = setTypeRandomElement(set,&ele,&llele);
// 将被取出元素从集合中删除
if (encoding == REDIS_ENCODING_INTSET) {
ele = createStringObjectFromLongLong(llele);
set->ptr = intsetRemove(set->ptr,llele,NULL);
} else {
incrRefCount(ele);
setTypeRemove(set,ele);
}
// 发送事件通知
notifyKeyspaceEvent(REDIS_NOTIFY_SET,"spop",c->argv[1],c->db->id);
/* Replicate/AOF this command as an SREM operation */
// 将这个命令当作 SREM 来传播,防止产生有害的随机性,导致数据不一致
// (不同的服务器随机删除不同的元素)
aux = createStringObject("SREM",4);
rewriteClientCommandVector(c,3,aux,c->argv[1],ele);
decrRefCount(ele);
decrRefCount(aux);
// 返回回复
addReplyBulk(c,ele);
// 如果集合已经为空,那么从数据库中删除它
if (setTypeSize(set) == 0) {
// 删除集合
dbDelete(c->db,c->argv[1]);
// 发送事件通知
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",c->argv[1],c->db->id);
}
// 发送键修改信号
signalModifiedKey(c->db,c->argv[1]);
// 将数据库设为脏
server.dirty++;
}