Redis 的内存回收机制
Redis 的内存回收机制分为 过期 Key 自动淘汰 和 内存淘汰策略 两个核心部分,它们共同管理内存使用,避免溢出。以下从底层原理详细解析:
一、过期 Key 自动淘汰
当 Key 设置 TTL(过期时间)后,Redis 通过 惰性删除 和 定期抽样删除 两种策略回收内存:
1. 惰性删除(Lazy Expiration)
- 原理:
- 访问 Key 时触发检查:每次读取 Key 前,先检查其是否过期,若过期则删除。
- 写操作也会触发惰性删除。
- 优点:对 CPU 友好,仅在访问时消耗资源。
- 缺点:若 Key 长期不被访问,会持续占用内存。
- 底层实现:
// Redis 源码(db.c) int expireIfNeeded(redisDb *db, robj *key) {if (!keyIsExpired(db,key)) return 0;// 删除过期 KeydeleteExpiredKeyAndPropagate(db,key);return 1; }
2. 定期抽样删除(Periodic Expiration)
- 原理:
- Redis 每秒执行 10 次(可配置)定时任务,每次随机抽取一定数量 Key 检查过期。
- 采用自适应算法:若某次抽样中过期 Key 比例超过 25%,则继续抽样,直到比例低于 25% 或超时。
- 优点:减少内存泄漏风险。
- 缺点:无法完全清理所有过期 Key。
- 核心配置:
hz 10 # 每秒执行定时任务次数
- 底层流程:
- 遍历所有数据库。
- 随机抽取
ACTIVE_EXPIRE_CYCLE_KEYS_PER_LOOP
(默认 20)个 Key。 - 删除过期 Key,统计删除数量。
- 若删除比例超过 25%,重复步骤 2。
二、内存淘汰策略(Eviction Policies)
当 Redis 内存达到 maxmemory
限制时,触发内存淘汰策略,删除 Key 以释放空间。策略分为 淘汰数据范围 和 淘汰算法 两个维度:
1. 淘汰数据范围
策略 | 说明 |
---|---|
volatile-* | 仅淘汰设置了 TTL 的 Key |
allkeys-* | 淘汰所有 Key(包括无 TTL 的 Key) |
2. 淘汰算法
(1)LRU(Least Recently Used)
- 原理:优先淘汰最久未访问的 Key。
- Redis 实现:
- 近似 LRU:随机采样 5(可配置)个 Key,淘汰其中最久未使用的。
- 节省内存:每个 Key 记录 24 位访问时间戳(精度为秒级)。
- 配置:
maxmemory-policy allkeys-lru maxmemory-samples 5
(2)LFU(Least Frequently Used)
- 原理:优先淘汰访问频率最低的 Key。
- Redis 实现:
- 使用 Morris 计数器:24 位存储访问频率(8 位记录计数器,16 位记录衰减时间)。
- 频率衰减机制:随时间降低计数器值,优先淘汰长期未访问的低频 Key。
- 配置:
maxmemory-policy allkeys-lfu lfu-log-factor 10 # 计数器增长难度 lfu-decay-time 1 # 计数器衰减时间(分钟)
(3)Random(随机淘汰)
- 原理:随机选择 Key 淘汰。
- 变种:
volatile-random
:仅淘汰带 TTL 的 Key。allkeys-random
:淘汰所有 Key。
(4)TTL(按过期时间淘汰)
- 原理:优先淘汰 TTL 最小的 Key(即将过期的 Key)。
- 配置:
maxmemory-policy volatile-ttl
三、定时内存淘汰的底层流程
当执行写入命令(如 SET
)且内存超限时,触发淘汰流程:
-
检查内存使用:
// Redis 源码(evict.c) int freeMemoryIfNeeded(void) {size_t mem_used = zmalloc_used_memory();if (mem_used <= server.maxmemory) return C_OK;// 触发淘汰 }
-
执行淘汰策略:
- 根据
maxmemory-policy
选择 Key 进行删除。 - 若为 LRU/LFU,调用
evictionPoolPopulate
采样并填充候选池:void evictionPoolPopulate(int dbid, dict *sampledict, dict *keydict, struct evictionPoolEntry *pool) {// 随机采样 Key 并排序 }
- 根据
-
异步删除:
- 使用惰性删除或后台线程(若启用
lazyfree-lazy-eviction
)释放内存。
- 使用惰性删除或后台线程(若启用
四、总结与对比
机制 | 触发条件 | 核心逻辑 | 性能影响 |
---|---|---|---|
惰性删除 | Key 被访问时 | 同步检查并删除过期 Key | 对单次操作有轻微延迟 |
定期抽样删除 | 定时任务(默认 10Hz) | 随机抽样删除过期 Key | 分散 CPU 消耗 |
内存淘汰策略 | 内存达到 maxmemory | 按策略(LRU/LFU/Random/TTL)淘汰 | 可能阻塞主线程 |
五、配置建议
- 优先使用
allkeys-lfu
:适用于热点数据场景,精准识别高频访问 Key。 - 调整采样数量:增大
maxmemory-samples
提高 LRU/LFU 准确性,但增加 CPU 消耗。 - 启用惰性删除:
lazyfree-lazy-eviction yes # 异步释放内存(减少主线程阻塞)
理解这些机制有助于优化 Redis 内存管理,平衡性能与资源利用率。