文章目录 Redis 内存管理与回收全景解析从底层分配模型到驱逐算法内核 核心基础底层结构与物理模型 1. 内存布局的基石redisObject 2. 内存分配器jemalloc 的分级对齐 核心原理机制拆解与失效本质⏳ 1. 过期键的删除策略双剑合璧️ 2. 内存淘汰策略八大驱逐模式与近似算法 生产调优与避坑实战️ 1. 内存淘汰策略maxmemory-policy选型指南 2. 过期清理与碎片率调优参数⚠️ 3. 生产环境 maxmemory 划线铁律与 BigKey 治理️ 面试回答思路结构化高分话术 Redis 内存管理与回收全景解析从底层分配模型到驱逐算法内核文章摘要本文深度剖析 Redis 的内存管理与回收机制。作为内存型数据库其极致性能高度依托于精密的内存架构。文章从底层内存分配器 jemalloc 与robj对象头切入揭示 Redis 如何通过数据结构对抗内存碎片进而逐层拆解过期键的双轨清理内核惰性删除与定期抽样与满载时的八大驱逐策略近似 LRU/LFU 算法的概率学平衡艺术并结合碎片优化与高并发调优场景为缓存架构提供硬核底层支撑。 核心基础底层结构与物理模型要理解 Redis 的内存消耗首先需要打破“存什么就是什么”的直观错觉。在 Redis 内存引擎中即使是最简单的一个字符串键值对也伴随着复杂的元数据嵌套。 1. 内存布局的基石redisObjectRedis 中的任何数据类型底层都由一个通用的结构体——redisObject对象头来承载。该结构体在 64 位系统上固定占用16 字节的内存空间typedefstructredisObject{unsignedtype:4;// 数据类型 (String, Hash, List等)占 4 bitunsignedencoding:4;// 内部编码格式 (raw, int, embstr等)占 4 bitunsignedlru:24;// 记录 LRU 时间戳 或 LFU 访问频次占 24 bit (3字节)intrefCount;// 引用计数器占 4 字节void*ptr;// 指向实际数据内存的指针占 8 字节 (64位系统)}robj;配合指向实际数据的指针如sds动态字符串内存呈现出经典的“指针套娃”模型。如果直接使用标准的 C 语言内存分配器如malloc海量的微小对象会带来极其恐怖的内存碎片率。 2. 内存分配器jemalloc 的分级对齐为了对抗碎片Redis 默认采用jemalloc内存分配器。jemalloc将内存空间划分为小、大、巨大几个类别并按特定阶梯如 8 字节、16 字节、32 字节直至几 KB 的 Chunk进行内存对齐。优势当业务申请 21 字节时jemalloc会直接分配 32 字节的内存块避免了频繁向操作系统申请带来的系统调用开销与严重碎片。 核心原理机制拆解与失效本质当内存触及maxmemory阈值或键到达生命周期时Redis 必须回答两个灵魂拷问已过期的键如何清理空间不够时谁将被驱逐首先需要明确的是过期键删除策略与内存淘汰策略是两个截然不同的底层机制过期键删除策略就像一位仓库管家在值钱的东西key过期失效后会找合适时机把它清理掉避免占据仓库空间。这是主动的、前置的维护工作内存淘汰策略则像一位应急处理员。当仓库实在塞不下新东西内存达到 maxmemory 上限时他必须当机立断扔掉一些现有物品无论是否过期来腾出地方。⏳ 1. 过期键的删除策略双剑合璧Redis 采用惰性删除与定期删除的组合拳来平衡 CPU 与内存惰性删除客户端每次访问某个 Key 时服务端会先检查其expires字典中的 TTL。若已过期立即触发del并返回空。这保证了 CPU 的极低开销但缺点是某些冷 Key 永不被访问会滞留内存。定期删除Redis 内部有一个定时任务默认每秒运行hz次通常为 10 次每次随机抽样 20 个设置了过期时间的 Key检查并清理其中已过期的 Key。若发现过期的比例超过 25%则循环该过程防止内存被过期死 Key 吞噬。️ 2. 内存淘汰策略八大驱逐模式与近似算法当 Redis 占用内存达到maxmemory上限时就会触发内存淘汰策略maxmemory-policy。我们可以通过“空间范围 筛选法则”这个二元矩阵来轻松理解这 8 种策略作用范围前缀allkeys-全库无差别对待所有键都在候选名单中。volatile-只对设置了过期时间TTL的有期徒刑区下手。noeviction特例死猪不怕开水烫直接拒绝写入返回 OOM。筛选法则后缀-lru最近最久没有被访问过“好久不见”原则。-lfu历史访问总频次最低的冷门货“冷落孤立”原则。-random盲盒抽签闭眼随机抓一个扔掉。-ttl剩余存活时间最短、马上要到期的优先淘汰。策略分类策略名称触发范围淘汰算法核心逻辑不淘汰noeviction全局默认策略。拒绝写入操作返回 OOM 错误仅允许读操作。LRU 策略volatile-lru设置过期时间在设置了过期时间的键中挑选最近最少使用的键淘汰。allkeys-lru全局在所有键中挑选最近最少使用的键淘汰最常用缓存策略。LFU 策略volatile-lfu设置过期时间在设置了过期时间的键中挑选最近使用频率最低的键淘汰。allkeys-lfu全局在所有键中挑选最近使用频率最低的键淘汰。随机策略volatile-random设置过期时间在设置了过期时间的键中随机挑选键淘汰。allkeys-random全局在所有键中随机挑选键淘汰。TTL 策略volatile-ttl设置过期时间挑选即将过期的键TTL 最小优先淘汰。底层内核为什么 Redis 不用精准 LRU/LFU精准的 LRU 需要维护一个巨大的双向链表每次读写都要调整节点位置这在单线程模型下会造成严重的性能雪崩。Redis 采用的是近似 LRU/LFU 算法随机采样一定数量的样本通过maxmemory-samples控制默认 5并在采样池中淘汰掉最旧或最不常用的键。同时object头中的 24 位lru字段高 16 位存储分钟级时间戳低 8 位存储对数计数器用于 LFU 衰减在微小代价下实现了极高的命中率收敛。 生产调优与避坑实战对内存管理与回收机制的透彻理解直接决定了高并发线上环境的稳定性上限。在实际的一线大厂生产环境中过期清理参数与内存淘汰策略必须结合具体业务进行精细化配置️ 1. 内存淘汰策略maxmemory-policy选型指南绝大多数纯缓存场景下我们通常会抛弃默认的noeviction转而采用**核心推荐allkeys-lru或allkeys-lfu**。这省去了强制要求业务给所有 Key 设置过期时间的繁琐当内存触顶时由 Redis 全局自动筛选淘汰冷门数据。LRU 与 LFU 的抉择若业务存在偶发的全量扫描如定时报表、批量拉取冷数据果断选择allkeys-lfu防止传统 LRU 因突发扫描把真正的热点 Key 挤出去。若访问模型均匀且符合时间局部性allkeys-lru则是性价比最高、最符合直觉的选择。 2. 过期清理与碎片率调优参数在高并发大内存如 64GB/128GB集群中默认参数往往需要微调hz每秒后台任务频率默认值10在过期 Key 极多时可能导致定期删除扫描不够及时。生产环境中通常会将其调高到30甚至50配置hz 50用极少量的 CPU 轮询代价换取更敏捷的内存回收避免突发 OOM。active-defrag-yes自动内存碎片清理大内存实例运行数周后jemalloc 的外部碎片率used_memory_rss / used_memory常会飙升。开启自动碎片整理可让 Redis 在后台利用微任务动态搬迁内存页active-defrag yes active-defrag-ignore-bytes 100mb # 碎片少于 100MB 时不触发 active-defrag-threshold-lower 10 # 碎片率超过 10% 时开始温和清理 active-defrag-threshold-upper 30 # 碎片率超过 30% 时全力清理这样既收缩了物理内存又对主线程响应延迟保持了极低的影响。⚠️ 3. 生产环境 maxmemory 划线铁律与 BigKey 治理容量规划黄金标准单台物理机/容器内存如果为 32GBRedis 的maxmemory最多给到20GB ~ 22GB。必须预留至少 30%~40% 的安全水位原因有二留给 jemalloc 内存分配器的外部碎片损耗。留给 RDB 快照BGSAVE和 AOF 重写BGREWRITEAOF时fork()子进程的写时复制Copy-on-Write, COW内存开销。如果不留富余水位一旦在写入高峰期触发持久化 fork物理内存瞬间打满直接会被 Linux 的 OOM Killer 强行杀死。避免 BigKey 导致的阻塞当 Hash 或 List 包含数百万个元素时同步删除会导致主线程长时间卡顿。自 Redis 4.0 起引入的UNLINK命令通过将底层内存回收交由后台线程Bio Thread异步处理彻底消除了主线程阻塞隐患。️ 面试回答思路结构化高分话术面试官提问“能讲讲 Redis 是怎么进行内存管理和淘汰的吗”你可以按照以下三步走逻辑进行降维打击式回答定基调物理承载与分配 “面试官您好Redis 的内存管理本质上是围绕着效率与空间的博弈展开的。首先所有数据在 Redis 中并不直接以原生类型存储而是被包裹在redisObject结构体中并通过jemalloc内存分配器进行按阶梯对齐分配从底层最大程度地减少内存碎片。”讲本质过期与淘汰的双轨机制 “在生命周期管理上过期键采用的是‘惰性删除 定期删除’组合拳保证了 CPU 与内存的高效剪裁而当内存触及maxmemory阈值时则会触发如allkeys-lru/lfu等内存淘汰策略。这里特别需要指出的是Redis 并没有采用高开销的精准 LRU/LFU 链表而是巧妙地在redisObject的 24 位lru字段中通过‘随机采样 对数计数器衰减’实现了近似算法。”谈性能与生产调优架构权衡与避坑 “在实际生产中我们不仅要结合业务合理规划maxmemory必须预留 30%~40% 应对 RDB/AOF 时的 COW 开销及 jemalloc 碎片通过调高hz与开启自动碎片整理来保障内存敏捷回收更要配合异步UNLINK避免 BigKey 阻塞从而在高并发下全面兼顾吞吐与系统稳定性。”以上, 就是本期的全部内容啦, 若有错误疏忽希望各位大佬及时指出制作不易, 希望能对各位提供微小的帮助, 可否留下你免费的赞呢