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++;
}