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函数添加元素。
相关推荐
自珍JAVA4 分钟前
Gobrs-Async 框架
后端
xdscode10 分钟前
Spring 依赖注入方式全景解析
java·后端·spring
青柠代码录18 分钟前
【Spring】@Component VS @Configuration
后端
喵个咪1 小时前
go-wind-cms 微服务架构设计:为什么基于 Kratos?
后端·微服务·cms
神奇小汤圆1 小时前
百度面试官:Redis 内存满了怎么办?你有想过吗?
后端
喵个咪1 小时前
Headless 架构优势:内容与展示解耦,一套 API 打通全端生态
前端·后端·cms
开心就好20251 小时前
HTTPS超文本传输安全协议全面解析与工作原理
后端·ios
小江的记录本1 小时前
【JEECG Boot】 JEECG Boot——数据字典管理 系统性知识体系全解析
java·前端·spring boot·后端·spring·spring cloud·mybatis
神奇小汤圆1 小时前
Spring Batch实战
后端
喵个咪1 小时前
传统 CMS 太笨重?试试 Headless 架构的 GoWind,轻量又强大
前端·后端·cms