Redis 过期删除与内存淘汰机制详解(基于 6.0.10 版本)
Redis 允许为键设置过期时间,但如何高效处理过期键、避免内存溢出,是其性能优化的核心问题。不同于简单的定时删除,Redis 采用定期删除 + 惰性删除的混合策略处理过期键,并在内存不足时通过淘汰机制释放空间。本文详细解析这两种机制的原理、配置及实践建议。
过期键的删除策略
Redis 未采用 “每个过期键对应一个定时器” 的定时删除策略(避免 CPU 资源耗尽),而是结合定期删除和惰性删除,在内存占用与 CPU 消耗之间取得平衡。
1. 惰性删除(Lazy Eviction)
- 核心逻辑:键过期后不主动删除,仅在被访问时(如
get
、hget
等命令)才检查是否过期。若过期,则删除该键并返回空;若未过期,则正常返回值。 - 优势:无需额外 CPU 资源监控过期键,仅在必要时执行删除,适合低频访问的过期键。
- 劣势:若过期键长期未被访问,会占用内存(“内存泄漏” 风险),需配合定期删除弥补。
2. 定期删除(Periodic Eviction)
- 核心逻辑:Redis 每隔一段时间(由hz配置控制,默认每秒 10 次)主动扫描部分过期键并删除,具体步骤:
- 从 “过期键字典”(专门存储设置了过期时间的键)中随机抽取 20 个键。
- 删除这 20 个键中已过期的键。
- 若过期键占比超过 25%,重复步骤 1(继续抽取删除),直至占比低于 25% 或达到最大扫描时间(默认 25ms)。
- 配置参数:
hz <num>
:控制定期扫描的频率(默认 10,即每秒 10 次)。值越大,扫描越频繁,过期键删除越及时,但 CPU 消耗越高。
- 优势:主动清理长期未访问的过期键,减少内存浪费。
- 注意:为避免扫描耗时过长阻塞服务,单次扫描时间被限制在 25ms 内,因此无法保证所有过期键都被及时删除。
3. 主从结构中的过期键处理
在主从复制中,过期键的删除存在特殊逻辑:
- 主服务器:采用上述 “定期 + 惰性” 策略正常删除过期键,并向从服务器发送
del
命令。 - 从服务器:即使键已过期,也不主动删除,仅在收到主服务器的
del
命令后才删除。 - 潜在问题:主从同步存在延迟时,从服务器可能返回已过期的键(主服务器已删除,但
del
命令未同步),导致短暂的数据不一致。
内存淘汰机制(Maxmemory Policy)
当 Redis 内存使用达到 maxmemory
配置的阈值时,会触发内存淘汰机制,主动删除部分键以释放空间。淘汰机制的核心是 “按规则筛选并删除键”,具体规则由 maxmemory-policy
配置。
核心配置
maxmemory <size>
:设置 Redis 最大可用内存(如maxmemory 1GB
),0 表示不限制(默认,生产环境需显式设置)。maxmemory-policy <policy>
:设置内存淘汰策略(默认noeviction
)。
8 种淘汰策略及适用场景
策略名称 | 淘汰规则 | 适用场景 |
---|---|---|
volatile-lru |
仅从设置了过期时间的键中,淘汰最近最少使用(LRU) 的键。 | 需保留非过期键(如核心配置),仅淘汰临时过期数据(如缓存)。 |
allkeys-lru |
从所有键中,淘汰最近最少使用(LRU) 的键。 | 所有键均为缓存数据,优先保留频繁访问的键(如热点商品)。 |
volatile-lfu |
仅从设置了过期时间的键中,淘汰最少频率使用(LFU) 的键。 | 需区分访问频率(如低频访问的临时数据)。 |
allkeys-lfu |
从所有键中,淘汰最少频率使用(LFU) 的键。 | 适合按访问频率区分优先级的场景(如用户行为日志,高频访问保留)。 |
volatile-random |
从设置了过期时间的键中,随机淘汰。 | 过期键访问频率均匀,无需区分优先级。 |
allkeys-random |
从所有键中,随机淘汰。 | 所有键重要性相近,随机淘汰影响最小(如临时会话数据)。 |
volatile-ttl |
从设置了过期时间的键中,淘汰剩余过期时间最短(TTL 最小) 的键。 | 需优先保留长期有效的过期键(如即将过期的促销活动数据优先淘汰)。 |
noeviction (默认) |
不淘汰任何键,写操作返回错误(读操作正常)。 | 不允许数据丢失的场景(如核心业务计数器),需手动扩容或清理。 |
LRU/LFU 算法的实现细节
(1)LRU(Least Recently Used,最近最少使用)
- 原理:优先淘汰最长时间未被访问的键。
- Redis 实现:
- 每个键的
redisObject
结构中包含一个 24bit 的lru
字段,记录最后一次访问的时间戳。 - 淘汰时采用采样淘汰:随机抽取
maxmemory-samples
个键(默认 5),淘汰其中lru
时间最早的键。 - 重复采样淘汰,直至内存低于
maxmemory
。
- 每个键的
(2)LFU(Least Frequently Used,最少频率使用)
- 原理:优先淘汰访问频率最低的键(6.0 版本新增)。
- Redis 实现:
- 每个键的
redisObject
结构中包含一个 24bit 的lfu
字段,高 16bit 记录最近访问时间,低 8bit 记录访问频率(计数器)。 - 频率计数器随访问递增,但会随时间衰减(避免长期低频键占据高位)。
- 每个键的
(3)采样率配置(maxmemory-samples
)
- 作用:控制 LRU/LFU 淘汰时的采样数量(默认 5)。
- 影响:
- 采样率越高(如 10),选出的 “最该淘汰的键” 越准确,但 CPU 消耗越高。
- 采样率越低(如 3),CPU 消耗低,但可能淘汰不该淘汰的键。
- 建议:默认 5 已平衡准确性与性能,无需频繁调整。
配置与实践建议
关键配置示例
1 | # 设置最大可用内存为 2GB |
通过命令动态修改:
1 | 设置最大内存为 1GB |
实践建议
- 避免大量键同时过期:定期删除时若大量键同时过期,可能导致扫描时间超过 25ms,阻塞服务。建议过期时间随机化(如
expire key 3600 + rand(0, 100)
)。 - 优先使用 LRU/LFU 策略:相比随机或 TTL 策略,LRU/LFU 更符合 “保留有用数据” 的需求,适合缓存场景。
- 主从环境注意内存差异:从服务器可能因未及时删除过期键而占用更多内存,建议从节点内存配置大于主节点。
- 监控内存使用率:通过
info memory
查看used_memory
和maxmemory
,确保内存使用率稳定在 80% 以下,避免频繁触发淘汰
v1.3.10