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

相关推荐
小杜-coding5 小时前
黑马点评day04(分布式锁-setnx)
java·spring boot·redis·分布式·spring·java-ee·mybatis
LLLLLindream7 小时前
Redis-商品缓存
数据库·redis·缓存
袁气满满~_~7 小时前
LeetCode:101、对称二叉树
算法·leetcode·职场和发展
天上掉下来个程小白7 小时前
缓存套餐-01.Spring Cache介绍和常用注解
java·redis·spring·缓存·spring cache·苍穹外卖
一眼青苔8 小时前
如何在MySQL中实现类似Redis的PING命令的功能来检测连接状态?
数据库·redis·mysql
码码哈哈0.08 小时前
2025最新:3分钟使用Docker快速部署单节点Redis
redis·docker·eureka
奔驰的小野码8 小时前
SpringAI实现AI应用-使用redis持久化聊天记忆
java·数据库·人工智能·redis·spring
flying_131410 小时前
面试常问系列(一)-神经网络参数初始化-之-softmax
深度学习·神经网络·算法·机器学习·面试
蒟蒻小袁11 小时前
力扣面试150题-- 翻转二叉树
算法·leetcode·面试
养一只Trapped_beast11 小时前
【LeetCode】删除排序数组中的重复项 II
算法·leetcode·职场和发展