知其然要知其所以然,探索每一个知识点背后的意义,你知道的越多,你不知道的越多,一起学习,一起进步,如果文章感觉对您有用的话,关注、收藏、点赞,有困惑的地方请评论,我们一起交流!
Redis 的 过期键删除策略 和 内存淘汰机制 是保障其高效内存管理的核心设计。以下从工作原理、实现细节到应用场景的深度分析:
一、过期键删除策略
Redis 通过 惰性删除 和 定期删除 两种策略协同工作,平衡性能与实时性。
1. 惰性删除(Lazy Expiration)
- 触发时机:当客户端访问一个键时,Redis 会检查该键是否过期。
- 执行流程:
- 客户端执行
GET key命令。 - Redis 先检查
key是否已过期(比较当前时间与键的expires字典中的过期时间)。 - 如果过期,则删除键并返回空;否则返回键值。
- 客户端执行
- 优点:对 CPU 友好,只在使用时消耗资源。
- 缺点:可能导致大量过期键长期占用内存(如不再访问的键)。
2. 定期删除(Active Expiration)
- 触发时机:Redis 周期性随机抽查部分过期键并删除。
- 执行流程:
- 从
expires字典中随机选择 20 个键。 - 删除其中已过期的键。
- 如果超过 25% 的键过期,则重复步骤 1。
- 从
- 核心配置:
hz参数(默认 10):控制每秒执行定期删除的频率。- 每次检查的键数量和时间上限由算法动态调整。
- 优点:减少内存浪费,平衡实时性。
- 缺点:仍可能遗漏部分过期键。
3. 过期键存储结构
- 过期字典(expires):一个哈希表,记录所有设置了过期时间的键及其到期时间戳。
- Key:指向实际键的指针。
- Value:64 位整数存储过期时间(Unix 时间戳)。
二、内存淘汰机制(Eviction Policies)
当内存达到 maxmemory 限制时,Redis 根据配置的策略淘汰键以释放空间。
1. 淘汰策略分类
Redis 提供 8 种策略,通过 maxmemory-policy 配置:
| 策略 | 规则 | 适用场景 |
|---|---|---|
noeviction(默认) | 拒绝所有写入操作(DEL 等可继续),返回 OOM 错误。 | 数据绝对不允许丢失的场景。 |
allkeys-lru(常用) | 从所有键中淘汰最近最少使用(LRU)的键。 | 热点数据分布明显的缓存。 |
volatile-lru | 从设置了过期时间的键中淘汰 LRU 键。 | 需保留持久化数据的缓存。 |
allkeys-lfu(常用) | 从所有键中淘汰最不频繁使用(LFU)的键(Redis 4.0+)。 | 长期保留高频访问键的场景。 |
volatile-lfu | 从过期键中淘汰 LFU 键。 | 结合 TTL 和访问频率的场景。 |
allkeys-random | 随机淘汰任意键。 | 数据访问无规律的场景。 |
volatile-random | 随机淘汰过期键。 | 需保留部分持久化数据的场景。 |
volatile-ttl | 优先淘汰剩余存活时间(TTL)最短的键。 | 需要快速清理短期数据的场景。 |
2. LRU 与 LFU 的实现优化
- 近似 LRU/LFU:Redis 采用 概率性采样 而非精确计算,以节省内存。
- 每次淘汰时随机选取 5 个(可配置)候选键,选择其中最久未使用(LRU)或最少使用(LFU)的键。
- 通过
maxmemory-samples参数调整采样数量(默认 5,增大更精确但消耗 CPU)。
- LFU 的访问频率统计:
- 使用 24 位计数器(Logarithmic Counter)记录访问频率,随时间衰减。
- 通过
lfu-log-factor和lfu-decay-time控制衰减速度。
3. 淘汰流程
- 客户端执行写入命令时触发内存检查。
- 若内存超限,根据策略选择待淘汰键。
- 删除键并释放内存,然后处理当前命令。
三、生产环境配置建议
1. 过期键管理
- 合理设置 TTL:避免大量键同时过期(导致瞬时负载)。
- 监控过期键:使用
INFO stats查看expired_keys计数。 - 调整定期删除频率:增大
hz(谨慎,会增加 CPU 负载)。
2. 内存淘汰优化
- 策略选择:
- 缓存场景:
allkeys-lru或allkeys-lfu。 - 持久化 + 缓存混合:
volatile-lru。 - 严格数据保护:
noeviction。
- 缓存场景:
- 关键配置:
maxmemory 4gb # 设置最大内存 maxmemory-policy allkeys-lru # 选择淘汰策略 maxmemory-samples 10 # 提高 LRU/LFU 精度
3. 大 Key 处理
- 避免大 Key:单个 String 不超过 10KB,集合元素不超过 5000。
- 拆分大 Key:如将
user:1:favorites拆分为多个小 Hash。
四、常见问题与解决方案
1. 内存溢出但大量键已过期
- 原因:惰性删除未触发,定期删除未清理完。
- 解决:手动执行
SCAN+TTL检查,或增加hz值。
2. 淘汰策略导致热点数据被误删
- 现象:
allkeys-random误删高频访问键。 - 解决:切换为
allkeys-lru或allkeys-lfu。
3. LFU 计数不准确
- 调整参数:
lfu-log-factor 10 # 降低计数器增长难度(默认 10) lfu-decay-time 1 # 计数器衰减时间(分钟,默认 1)
五、总结:Redis 内存管理设计哲学
- 惰性删除:优先保证性能,牺牲部分内存效率。
- 定期删除:平衡实时性与 CPU 开销。
- 近似 LRU/LFU:以极小内存开销实现高效淘汰。
- 灵活策略:根据业务特点选择淘汰逻辑。