• Redis 6.0源码学习 String类型


    Redis 6.0源码学习 String类型

      String字符串类型是RedisObject中最基础的一个类型,相对应的编码格式有3种,分别是OBJ_ENCODING_RAW、OBJ_ENCODING_INT和OBJ_ENCODING_EMBSTR。

    前置知识

    Redis 6.0源码学习 RedisObject

    内部编码

    OBJ_ENCODING_INT

      OBJ_ENCODING_INT算是所有编码格式中最特殊的一个。OBJ_ENCODING_INT编码中,redisObject中指针属性ptr存储的数据是字面量而不是内存地址。由于复用了ptr属性,所以OBJ_ENCODING_INT只能存储8位字节的长整型。

    set key 8653
    object encoding key
    
    • 1
    • 2

      在内存优化方面,OBJ_ENCODING_INT编码还做了享元优化。当服务器初始化的时候,会调用service.c中的createSharedObjects函数。

    代码片段 初始化createSharedObjects.integers

    for (j = 0; j < OBJ_SHARED_INTEGERS; j++) {
        shared.integers[j] =
            makeObjectShared(createObject(OBJ_STRING,(void*)(long)j));
        shared.integers[j]->encoding = OBJ_ENCODING_INT;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    OBJ_ENCODING_RAW

      OBJ_ENCODING_RAW是一种非常中规中矩的实现,它的ptr指针指向了一个SDS实例。

    set key "这是一个等于45个字符的字符串...."
    object encoding key
    
    • 1
    • 2

    OBJ_ENCODING_EMBSTR

      OBJ_ENCODING_RAW中robj与sds内存空间不是连续的,基于局部性思想就诞生了robj与sds采用连续内存空间的OBJ_ENCODING_EMBSTR。当字符串长度不大于44个字符时,会采用OBJ_ENCODING_RAW编码。

    set key "hello,world"
    object encoding key
    
    • 1
    • 2

    编码推断

      setCommand是set命令的实现函数。以"SET a 111"为例,解析完请求之后,111对应的redisObject是编码为OBJ_ENCODING_RAW的字符串。tryObjectEncoding会进行一次编码推断,如果符合OBJ_ENCODING_INT的约束,还可能从享元池中复用对象。

    代码片段 t_string.c setCommand

    void setCommand(client *c) {
        int j;
        robj *expire = NULL;
        int unit = UNIT_SECONDS;
        int flags = OBJ_SET_NO_FLAGS;
    
        ...
        ...
        ...
    
        c->argv[2] = tryObjectEncoding(c->argv[2]);
        setGenericCommand(c,flags,c->argv[1],c->argv[2],expire,unit,NULL,NULL);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    代码片段 object.c tryObjectEncoding

    robj *tryObjectEncoding(robj *o) {
        long value;
        sds s = o->ptr;
        size_t len;
    
        /* Make sure this is a string object, the only type we encode
         * in this function. Other types use encoded memory efficient
         * representations but are handled by the commands implementing
         * the type. */
        serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
    
        /* We try some specialized encoding only for objects that are
         * RAW or EMBSTR encoded, in other words objects that are still
         * in represented by an actually array of chars. */
        if (!sdsEncodedObject(o)) return o;
    
        /* It's not safe to encode shared objects: shared objects can be shared
         * everywhere in the "object space" of Redis and may end in places where
         * they are not handled. We handle them only as values in the keyspace. */
         if (o->refcount > 1) return o;
    
        /* Check if we can represent this string as a long integer.
         * Note that we are sure that a string larger than 20 chars is not
         * representable as a 32 nor 64 bit integer. */
        len = sdslen(s);
        if (len <= 20 && string2l(s,len,&value)) {
            /* This object is encodable as a long. Try to use a shared object.
             * Note that we avoid using shared integers when maxmemory is used
             * because every object needs to have a private LRU field for the LRU
             * algorithm to work well. */
            if ((server.maxmemory == 0 ||
                !(server.maxmemory_policy & MAXMEMORY_FLAG_NO_SHARED_INTEGERS)) &&
                value >= 0 &&
                value < OBJ_SHARED_INTEGERS)
            {
                decrRefCount(o);
                incrRefCount(shared.integers[value]);
                return shared.integers[value];
            } else {
                if (o->encoding == OBJ_ENCODING_RAW) {
                    sdsfree(o->ptr);
                    o->encoding = OBJ_ENCODING_INT;
                    o->ptr = (void*) value;
                    return o;
                } else if (o->encoding == OBJ_ENCODING_EMBSTR) {
                    decrRefCount(o);
                    return createStringObjectFromLongLongForValue(value);
                }
            }
        }
    
        /* If the string is small and is still RAW encoded,
         * try the EMBSTR encoding which is more efficient.
         * In this representation the object and the SDS string are allocated
         * in the same chunk of memory to save space and cache misses. */
        if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT) {
            robj *emb;
    
            if (o->encoding == OBJ_ENCODING_EMBSTR) return o;
            emb = createEmbeddedStringObject(s,sdslen(s));
            decrRefCount(o);
            return emb;
        }
    
        /* We can't encode the object...
         *
         * Do the last try, and at least optimize the SDS string inside
         * the string object to require little space, in case there
         * is more than 10% of free space at the end of the SDS string.
         *
         * We do that only for relatively large strings as this branch
         * is only entered if the length of the string is greater than
         * OBJ_ENCODING_EMBSTR_SIZE_LIMIT. */
        trimStringObjectIfNeeded(o);
    
        /* Return the original object. */
        return o;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
  • 相关阅读:
    java面试题:java中的单例设计模式及两种实现方法的代码举例
    并查集
    Python 基础30道测试题「答案」
    CMake库搜索函数居然不搜索LD_LIBRARY_PATH
    SpringCloud
    2、《创建您自己的NFT集合并发布一个Web3应用程序来展示它们》启动并运行您的本地环境
    docker搭建本地仓库
    Json-Jackson和FastJson
    我这样写代码,比直接使用 MyBatis 效率提高了 100 倍
    JAVA计算机毕业设计在线直播平台Mybatis+源码+数据库+lw文档+系统+调试部署
  • 原文地址:https://blog.csdn.net/qq1620657419/article/details/126037259