• Redis 底层对 String 的 3 个优化


    Redis对 String 类型实现了很多优化,通过以下三个重要的优化点来解释:

    1. 简单动态字符串(SDS

    Redis 的 String 类型内部采用简单动态字符串(SDS)来管理字符串。相比于 C 语言的原生字符串,SDS 有以下优势:

    • 能够动态地改变自己的长度,避免了不必要的内存分配和拷贝操作;
    • SDS 本身有一个长度属性,可以快速计算字符串的长度;
    • SDS 可以与 C 语言原生字符串互相转换,方便对 C 语言中一些函数的使用。

    SDS 源码:

    struct sdshdr {
        int len;                // 字符串实际长度
        int free;               // 字符串剩余空间
        char buf[];             // 字符串数据
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

    源码解释:redis 源码实现 sds 函数,定义了 SDS 的各种操作函数,例如 sdsnew() 函数用于新建 SDS 字符串,sdsempty() 函数用于初始化一个空 SDS 字符串等。

    2. 共享字符串

    当多个客户端传入相同的字符串参数时,Redis 会把相同字符串的内部指针指向同一内存地址,这些指针共享同一个字符串对象。这种优化可以减少内存占用,提高性能。

    robj *lookupShared(const char *s, size_t len) {
        dictEntry *de = dictFind(server.shared.dict,s);
        if (de) {
            robj *shared = dictGetVal(de);
            return shared;
        }
    
        robj *shared = createStringObject(s,len);
        dictAdd(server.shared.dict, s, shared);
        return shared;
    }
    
    robj *createShared(const char *ptr, size_t len) {
        robj *sobj = createObject(OBJ_STRING,sdsnewlen(ptr,len));
        sobj->encoding = OBJ_ENCODING_RAW; // 设置编码方式为RAW
        return sobj;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    源码解释:这里有两个相关函数:lookupShared() 用于在 Redis 的共享字符串池中查找指定的字符串,如果字符串存在,则返回字符串对象;如果字符串不存在,则新建字符串对象并存储在共享字符串池中。createShared() 函数则是用于创建新的共享字符串对象。

    3. 编码的优化

    Redis 的 String 类型支持多种编码方式,如 INT、EMBSTR、RAW 等。根据不同的编码方式和数据类型,Redis 选择最适合的编码方式来储存数据,从而避免了冗余的存储空间。

    int encodingType(robj *o) {
        if (o->encoding == OBJ_ENCODING_INT) {
            return OBJ_ENCODING_INT;
        } else if (o->encoding == OBJ_ENCODING_EMBSTR) {
            return OBJ_ENCODING_EMBSTR;
        } else {
            return OBJ_ENCODING_RAW;
        }
    }
    
    void setStringObject(robj *o, const char *s, size_t len) {
        if (o->encoding == OBJ_ENCODING_INT) {
            // 释放原有的整数值空间
            decrRefCount(o);
            // 创建新的Raw编码字符串对象
            o = createStringObject(s,len);
        } else if (o->encoding == OBJ_ENCODING_EMBSTR) {
            // 释放原有的Embstr编码字符串空间
            sdsfree(o->ptr);
            o->ptr = zmalloc(len);
            memcpy(o->ptr, s, len);
            o->encoding = OBJ_ENCODING_RAW;
        } else {
            // 设置Raw编码字符串值
            o->ptr = sdscpylen(o->ptr, s, len);
            o->encoding = OBJ_ENCODING_RAW;
        }
    }
    
    • 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

    源码解释:这里有两个相关函数:encodingType() 用于返回某个值采用的编码方式,可以检查一个字符串对象或列表对象采用的编码方式;另一个函数 setStringObject() 用于设置一个字符串对象的值,根据需要选择适当的编码方式。

    4. 总结时刻

    回答出来以上三点,表明你对 Redis 的 String 类型的源码是有研究的,而不是每天只顾 CRUD,只顾写业务。

    另外,也能表明你的技术深度,面试官一定会对你另眼相看哟!加油!

  • 相关阅读:
    Kamailio default-routing-logic-flowchart
    ESP8266-Arduino编程实例-TSL2591数光转换器驱动
    springboot毕设项目超市进销存管理系统7cq1l(java+VUE+Mybatis+Maven+Mysql)
    【Vue3 源码解析】reactive 全家桶
    SOCKS5代理(源码)
    SpringBoot约定大于配置
    加强版python连接飞书通知——本地电脑PC端通过网页链接打开本地已安装软件(调用注册表形式,以漏洞扫描工具AppScan为例)
    java多线程之Lock锁原理以及案例实现电影院卖票
    Waline评论服务docker自部署手册 + 无需备案域名配置
    8Base集团通过SmokeLoader部署新的Phobos勒索软件变种
  • 原文地址:https://blog.csdn.net/Prepared/article/details/133943760