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

相关推荐
Y编程小白26 分钟前
Redis可视化工具--RedisDesktopManager的安装
数据库·redis·缓存
好评笔记2 小时前
AIGC视频扩散模型新星:Video 版本的SD模型
论文阅读·深度学习·机器学习·计算机视觉·面试·aigc·transformer
程序员小灰2 小时前
当了leader才发现,大厂最想裁掉的,不是上班总迟到的,也不是下班搞失联的,而是经常把这3句话挂在嘴边的!
面试
无限码力3 小时前
路灯照明问题
数据结构·算法·华为od·职场和发展·华为ode卷
东软吴彦祖3 小时前
包安装利用 LNMP 实现 phpMyAdmin 的负载均衡并利用Redis实现会话保持nginx
linux·redis·mysql·nginx·缓存·负载均衡
言之。4 小时前
【Java】面试中遇到的两个排序
java·面试·排序算法
DZSpace5 小时前
使用 Helm 安装 Redis 集群
数据库·redis·缓存
言之。5 小时前
【面试】Java 记录一次面试过程 三年工作经验
java·面试·职场和发展
Pandaconda6 小时前
【Golang 面试题】每日 3 题(三十九)
开发语言·经验分享·笔记·后端·面试·golang·go
github_czy10 小时前
(k8s)k8s部署mysql与redis(无坑版)
redis·容器·kubernetes