【后端面试总结】Redis字符串实现原理

字符串是我们平时接触频率最高的一个基础类型,但就是这么一个平平无奇的基本类型,在Redis里面也是经历了各种各样的优化,来优化它对内存的占用,了解这部分内容,与其说是"学习Redis",不如说是"向Redis学习",学习Redis从各个可能的角度,来优化内存使用的方法和不放过任何一个可能的内存优化项的态度。

Redis的字符串叫"SDS",也就是Simple Dynamic String。它的结构是一个带长度信息的字节数组。

Go 复制代码
struct SDS<T> {
    T capacity;        // 数组容量
    T len;             // 数组长度
    byte flags;        // 特殊标志位,不用理睬它
    byte[] content;    // 数组内容
}

上面的SDS结构使用了泛型T。为什么不直接用int呢?因为当字符串比较短时,len和capacity可以使用byte和short来表示,Redis为了对内存做极致的优化,不同长度的字符串使用不同的结构体来表示。

Redis的字符串有两种存储方式,在长度特别短时,使用embstr形式存储,而当长度超过44字节时,使用raw形式存储。

为了解释这种现象,我们首先来了解一下Redis对象头结构,所有的Redis对象都有下面的这个头结构。

Go 复制代码
struct RedisObject {
    int4 type;          // 4 bits
    int4 encoding;      // 4 bits
    int24 lru;          // 24 bits
    int32 refcount;     // 4 bytes
    void *ptr;          // 8 bytes, 64-bit system
}

不同的对象具有不同的类型type(4bit)。同一个类型的type会有不同的存储形式encoding(4bit)。为了记录对象的LRU信息,使用了24个bit来记录LRU信息。每个对象都有个引用计数,当引用计数为0时,对象就会被销毁,内存被回收。ptr指针将指向对象内容(body)的具体存储位置。这样一个RedisObject对象头结构需要占据16字节的存储空间。

接着我们再看SDS结构体的大小,在字符串比较小时,SDS对象头结构的大小是capacity+3

,至少是3字节。意味着分配一个字符串的最小空间占用为19(即16+3)字节。

Go 复制代码
struct SDS {
    int8 capacity;        // 1 byte
    int8 len;             // 1 byte
    int8 flags;           // 1 byte
    byte[] content;       // 内联数组,长度为capacity
}

embstr将RedisObject对象头结构和SDS对象连续存在一起,使用malloc方法一次分配,而raw存储形式不一样,它需要两次malloc方法,两个对象头在内存地址上一般是不连续的。

内存分配器jemalloc、tcmalloc等分配内存大小的单位都是2/4/8/16/32/64字节等,为了能容纳一个完整的embstr对象,jemalloc最少会分配32字节的空间,如果字符串再稍微长一点,那就是64自己的空间。如果字符串总体超出了64字节,Redis认为它是一个大字符串,不再适合使用embstr存储,而该使用raw形式。

当内存分配了64字节空间时,那这个字符串长度最大可以是多少呢?这个长度就是44字节。

为什么是44字节呢?64字节中,除了RedisObject的16字节和SDS的3字节,留给content的长度最多只有45(即64 - 19)自己饿了。字符串又是以NULL结尾,所以embstr形式最大能容纳的字符串长度就是44字节。

扩容策略:

在字符串长度小于1MB之前,扩容空间采用加倍策略,也就是保留100%的冗余空间。当字符串长度超过1MB之后,为了避免加倍后的冗余空间过大而导致浪费,每次扩容只会多分配1MB大小的冗余空间。

相关推荐
Ruihong12 小时前
Vue withDefaults 转 React:VuReact 怎么处理?
vue.js·react.js·面试
kyriewen13 小时前
别再这样写 async/await 了:我在 Code Review 中见过最多的 8 个错误
前端·javascript·面试
烬羽18 小时前
字符串算法入门:从反转字符串到回文判断,面试不再慌
算法·面试
云技纵横18 小时前
一个 @Async,把 @Transactional 的事务边界打穿了
后端·面试
想要成为糕糕手19 小时前
Harness Engineering:大模型时代的“马鞍”——从记忆层开始,让AI真正为你所用
面试·ai编程·claude
kyriewen1 天前
我手写了一个 EventEmitter,面试官追问了 6 个问题——第 4 个我没答上来
前端·javascript·面试
她的男孩1 天前
后台接口加密别只会 HTTPS,ForgeAdmin 的 RSA + SM4/AES 源码拆解
后端·面试·开源
Randyliu1 天前
20260508-Agent搭建记录以及对ReAct框架的理解
面试·agent
ZzT2 天前
公司用 AI 筛简历,他写了个 AI 帮你挑公司
面试·aigc·ai编程
PBitW2 天前
GPT训练我的第四天,被打惨了!!!😭😭😭
前端·javascript·面试