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函数添加元素。
相关推荐
一弓虽7 分钟前
SpringBoot 学习
java·spring boot·后端·学习
姑苏洛言15 分钟前
扫码小程序实现仓库进销存管理中遇到的问题 setStorageSync 存储大小限制错误解决方案
前端·后端
光而不耀@lgy30 分钟前
C++初登门槛
linux·开发语言·网络·c++·后端
方圆想当图灵1 小时前
由 Mybatis 源码畅谈软件设计(七):SQL “染色” 拦截器实战
后端·mybatis·代码规范
毅航1 小时前
MyBatis 事务管理:一文掌握Mybatis事务管理核心逻辑
java·后端·mybatis
我的golang之路果然有问题1 小时前
速成GO访问sql,个人笔记
经验分享·笔记·后端·sql·golang·go·database
柏油2 小时前
MySql InnoDB 事务实现之 undo log 日志
数据库·后端·mysql
写bug写bug3 小时前
Java Streams 中的7个常见错误
java·后端
Luck小吕4 小时前
两天两夜!这个 GB28181 的坑让我差点卸载 VSCode
后端·网络协议
M1A14 小时前
全栈开发必备:Windows安装VS Code全流程
前端·后端·全栈