【后端面试总结】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大小的冗余空间。

相关推荐
uhakadotcom12 分钟前
BPF编程入门:使用Rust监控CPU占用
后端·面试·github
小样vvv20 分钟前
【面试篇】JVM
jvm·面试·职场和发展
Hyyy30 分钟前
ElementPlus按需加载 + 配置中文避坑(干掉1MB冗余代码)
前端·javascript·面试
uhakadotcom31 分钟前
GHSL-2024-252: Cloudflare Workers SDK 环境变量注入漏洞解析
后端·面试·github
uhakadotcom32 分钟前
GHSL-2024-264_GHSL-2024-265: 了解 AWS CLI 中的正则表达式拒绝服务漏洞 (ReDoS)
后端·面试·github
uhakadotcom38 分钟前
了解Chainlit:简化AI应用开发的Python库
后端·面试·github
lovebugs1 小时前
K8s面试第一篇:初识Kubernetes——核心概念与组件详解
后端·算法·面试
用户3315489111071 小时前
【零停机】一次400万用户数据的双写迁移技术详解
java·面试
逆袭的小黄鸭1 小时前
深入剖析 JavaScript 执行上下文:代码运行的幕后机制
前端·javascript·面试
AronTing1 小时前
05-Spring Security 认证与授权机制源码解析
后端·面试