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 分钟前
使用openapi生成前端请求文件报错 ‘Token “Integer“ does not exist.‘
java·前端·spring boot·后端·restful·个人开发
liuxin3344556624 分钟前
教育技术革新:SpringBoot在线教育系统开发
数据库·spring boot·后端
bug菌1 小时前
Java GUI编程进阶:多线程与并发处理的实战指南
java·后端·java ee
夜月行者3 小时前
如何使用ssm实现基于SSM的宠物服务平台的设计与实现+vue
java·后端·ssm
Yvemil73 小时前
RabbitMQ 入门到精通指南
开发语言·后端·ruby
sdg_advance3 小时前
Spring Cloud之OpenFeign的具体实践
后端·spring cloud·openfeign
猿java4 小时前
使用 Kafka面临的挑战
java·后端·kafka
碳苯4 小时前
【rCore OS 开源操作系统】Rust 枚举与模式匹配
开发语言·人工智能·后端·rust·操作系统·os
kylinxjd4 小时前
spring boot发送邮件
java·spring boot·后端·发送email邮件
2401_857439697 小时前
Spring Boot新闻推荐系统:用户体验优化
spring boot·后端·ux