Redis源码阅读之sadd

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

逻辑如下:

  1. 通过lookupKeyWrite函数查找key,获取值为set。
  2. 如果set为NULL,则调用setTypeCreate函数创建一个set对象,然后调用dbAdd函数将key添加到全局字典中。
  3. 遍历参数列表,对每个参数调用setTypeAdd函数添加到set中。
  4. 如果添加了元素,则调用signalModifiedKey函数发送信号,调用notifyKeyspaceEvent函数发送事件通知,更新server.dirty(变更次数统计)。
  5. 最后调用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;
}

逻辑为:

  1. 如果值可以转换为long long,则调用createIntsetObject,创建一个intset类型对象。
  2. 否则,调用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;
}

逻辑为:

  1. 判断subject的编码类型,如果是dict类型,则调用dictAddRaw函数添加元素。
  2. 添加成功后,调用dictSetKey和dictSetVal函数设置key和val。key为要添加的值,val为NULL。
  3. 如果是initset类型,则判断值是否可以转换为整数,则调用intsetAdd函数添加元素。

整数集合 中最多有1G个键。

  1. 如果值不可以转换为整数,则调用setTypeConvert函数将集合对象转换为dict类型,然后调用dictAdd函数添加元素。
相关推荐
Estar.Lee8 分钟前
查手机号归属地免费API接口教程
android·网络·后端·网络协议·tcp/ip·oneapi
2401_857610032 小时前
SpringBoot社团管理:安全与维护
spring boot·后端·安全
凌冰_2 小时前
IDEA2023 SpringBoot整合MyBatis(三)
spring boot·后端·mybatis
码农飞飞2 小时前
深入理解Rust的模式匹配
开发语言·后端·rust·模式匹配·解构·结构体和枚举
一个小坑货2 小时前
Rust 的简介
开发语言·后端·rust
monkey_meng3 小时前
【遵守孤儿规则的External trait pattern】
开发语言·后端·rust
Estar.Lee3 小时前
时间操作[计算时间差]免费API接口教程
android·网络·后端·网络协议·tcp/ip
新知图书4 小时前
Rust编程与项目实战-模块std::thread(之一)
开发语言·后端·rust
盛夏绽放4 小时前
Node.js 和 Socket.IO 实现实时通信
前端·后端·websocket·node.js
Ares-Wang4 小时前
Asp.net Core Hosted Service(托管服务) Timer (定时任务)
后端·asp.net