Redis中删除策略的几种实现方式
作者:LQ深蹲不写BUG
前言
要全面、深入理解 Redis 的删除策略,需从设计背景、核心分类(过期键删除策略 + 内存淘汰策略)、内部实现细节、与持久化的关联及实践选择五个维度展开。Redis 的删除策略本质是为了解决 “内存有限” 与 “性能优先” 的核心矛盾 —— 既要避免内存溢出,又要减少删除操作对单线程主线程的阻塞,同时保证数据一致性。
一、设计背景:为什么需要删除策略?
Redis 是内存数据库,内存资源昂贵且有限,若不主动管理内存,会导致内存溢出(OOM);同时,Redis 支持为键设置过期时间(TTL),需保证过期键能被合理清理;此外,Redis 是单线程模型,删除操作若过于耗时,会阻塞主线程,导致响应延迟。因此,Redis 设计了两类互补的删除策略:
- 过期键删除策略:主动处理 “已过期但未删除” 的键,减少无效内存占用;
- 内存淘汰策略:当内存达到上限时,被动删除部分键以释放内存,保证服务可用性。
二、第一类:过期键的 3 种核心删除策略
Redis 通过过期字典(expires dict) 单独存储所有键的过期时间(key 为目标键,value 为过期时间戳),所有过期键删除策略均基于此字典实现。三种策略各有优劣,Redis 最终采用 “惰性删除 + 定期删除” 的组合方案。

1. 定时删除(Timed Delete):“实时清理,CPU 杀手”
定义:为每个设置了过期时间的键,创建一个定时器(Timer),当键的过期时间到达时,定时器立即触发,执行DEL命令删除该键。
实现逻辑:
- 当调用
EXPIRE key ttl时,Redis 不仅在过期字典中记录过期时间,还会注册一个定时器; - 定时器到期后,直接在主线程中执行删除操作。
优缺点:
| 优点 | 缺点 |
|---|---|
| 内存最 “干净”:过期键立即删除,无无效内存占用; | CPU 压力大:若存在大量过期键,定时器密集触发,会占用大量 CPU 资源,导致主线程阻塞,影响 Redis 响应速度; |
| 数据一致性高:不会出现 “读取到过期键” 的情况; | 定时器管理成本高:每个过期键对应一个定时器,内存开销也会增加。 |
适用场景:
几乎不适用。Redis 是单线程模型,定时删除的 CPU 开销会严重影响服务性能,因此 Redis未采用此策略。
2. 惰性删除(Lazy Delete):“用的时候再删,内存隐患”
定义:Redis 不主动删除过期键,而是在访问键的瞬间(如GET、HGET、SETNX等操作),才检查该键是否过期:
- 若未过期:正常返回键值;
- 若已过期:执行
DEL命令删除该键,返回nil(空)。
实现逻辑:
以GET key为例:
- 先检查
key是否存在于过期字典中; - 若不存在:直接返回键值;
- 若存在:对比当前时间与过期时间戳,判断是否过期;
- 若已过期:删除
key(从数据库字典和过期字典中同时移除),返回nil; - 若未过期:返回键值。
优缺点:
| 优点 | 缺点 |
|---|---|
| CPU 友好:仅在 “访问过期键” 时才执行删除,无额外 CPU 开销,不影响主线程正常请求; | 内存泄漏风险:若大量过期键长期不被访问,会一直占用内存(“僵尸键”),导致内存利用率低,甚至触发内存上限; |
| 实现简单:无需管理定时器,逻辑轻量; | 可能出现 “瞬时过期键”:若过期键未被访问,其他操作(如KEYS、DBSIZE)会统计到这些过期键,导致数据统计不准确。 |
3. 定期删除(Periodic Delete):“折中方案,平衡 CPU 与内存”
定义: Redis 每隔一段时间(默认 100ms),主动扫描部分过期键并删除,扫描频率和范围由配置控制,避免一次性扫描所有过期键导致阻塞。
核心实现逻辑(Redis 6.0+)
- 扫描频率:由
hz配置控制(默认hz 10,即每秒执行 10 次定期扫描,每次间隔约 100ms); - 扫描范围:每次扫描不遍历所有过期键,而是采用 “采样 + 限制时间” 的方式:
- 从过期字典中随机抽取
N个键(默认N=20); - 检查这些键是否过期,删除已过期的键;
- 若删除的键占比超过
25%(即删除数≥5),则继续抽取N个键重复扫描; - 若占比≤25%,或扫描时间超过
2ms(避免阻塞主线程),则停止本次扫描。
- 从过期字典中随机抽取
优缺点:
| 优点 | 缺点 |
|---|---|
| 平衡 CPU 与内存:既避免了定时删除的 CPU 压力,也缓解了惰性删除的内存泄漏问题; | 扫描参数难调优:hz、N、25%阈值需根据业务调整,若hz过高或N过大,会增加 CPU 负担;若过低,则内存清理不及时; |
非阻塞设计:单次扫描时间限制在2ms内,不影响主线程处理请求; | 仍有 “漏删” 可能:部分过期键可能在两次扫描间隔内未被访问,也未被扫描到,暂时占用内存。 |
4. Redis 的最终选择:惰性删除 + 定期删除
两种策略互补,覆盖大部分场景:
- 定期删除:主动 “批量清理” 过期键,减少 “僵尸键” 数量,缓解内存压力;
- 惰性删除:兜底清理 “漏网之鱼”,确保访问时不会返回过期键,保证数据有效性。
例如:一个过期键未被定期扫描删除,但用户访问时,惰性删除会立即清理它;反之,若过期键长期不被访问,定期扫描会逐步清理它。
三、第二类:内存淘汰策略(Maxmemory Eviction)
即使有过期键删除策略,Redis 仍可能因 “大量未设置过期时间的键” 或 “过期键删除不及时” 导致内存达到上限(maxmemory配置)。此时,Redis 会触发内存淘汰策略,删除部分键以释放内存,避免 OOM。
1. 核心前提:maxmemory配置
- 默认值:
0(即不限制内存,仅受系统内存限制,生产环境必须手动设置,如maxmemory 4gb); - 触发时机:当 Redis 使用的内存(包括键值对、过期字典、缓冲区等)达到
maxmemory时,触发淘汰策略(仅对 “写操作” 触发,读操作不受影响)。
2. 8 种内存淘汰策略(Redis 5.0+)
根据 “淘汰范围” 和 “淘汰算法”,分为 4 类,核心区别是 “是否只淘汰过期键” 和 “用什么规则淘汰”:
| 策略常量 | 淘汰范围 | 淘汰算法 | 适用场景 |
|---|---|---|---|
| noeviction(默认) | 无(不淘汰任何键) | - | 禁止淘汰,内存满时拒绝所有写请求(返回OOM command not allowed),适合不允许数据丢失的场景(如核心配置存储)。 |
| volatile-lru | 仅淘汰 “设置了过期时间” 的键 | LRU(最近最少使用) | 希望保留常用的过期键,淘汰不常用的,适合有明确过期时间且需优先保留热点数据的场景(如缓存用户会话)。 |
| allkeys-lru | 所有键(无论是否过期) | LRU(最近最少使用) | 不清楚哪些键常用,希望淘汰长期未访问的键,适合通用缓存场景(如商品详情缓存)。 |
| volatile-lfu | 仅淘汰 “设置了过期时间” 的键 | LFU(最不经常使用) | 比 LRU 更精准(统计 “访问频率” 而非 “最近访问时间”),适合淘汰低频访问的过期键(如低频访问的活动页面缓存)。 |
| allkeys-lfu | 所有键(无论是否过期) | LFU(最不经常使用) | 适合需要精准淘汰 “低频率访问” 键的场景(如内容推荐系统,淘汰很少被点击的内容)。 |
| volatile-random | 仅淘汰 “设置了过期时间” 的键 | 随机淘汰 | 适合对淘汰键无明确优先级,仅需释放内存的场景(较少用)。 |
| allkeys-random | 所有键(无论是否过期) | 随机淘汰 | 适合数据无热点、淘汰任意键均可的场景(极少用)。 |
| volatile-ttl | 仅淘汰 “设置了过期时间” 的键 | 淘汰 “剩余 TTL 最短” 的键 | 希望尽快删除快过期的键,释放内存给新键,适合需优先保留 “剩余时间长” 的过期键的场景(如短期活动缓存,优先保留刚创建的活动数据)。 |
maxmemory最大可使用内存 占用物理内存的比例,默认值为0,表示不限制,生产环境中根据需求设定,通常设置在50%以上。
maxmemory-samples每次选取待删除数据的个数 选取数据时并不会全库扫描,导致严重的性能消耗,降低读写性能。因此采用随机获取数据的方式 作为待检测删除数据
maxmemory-policy删除策略
检测易失数据(可能会过期的数据集server.db[i].expires )
① volatile-lru:挑选最近最少使用的数据淘汰
② volatile-lfu:挑选最近使用次数最少的数据淘汰
③ volatile-ttl:挑选将要过期的数据淘汰
④ volatile-random:任意选择数据淘汰 检测全库数据(所有数据集server.db[i].dict )
⑤ allkeys-lru:挑选最近最少使用的数据淘汰
⑥ allkeys-lfu:挑选最近使用次数最少的数据淘汰
⑦ allkeys-random:任意选择数据淘汰 放弃数据驱逐
⑧ no-enviction(驱逐):禁止驱逐数据(redis4.0中默认策略),会引发错误OOM(Out Of Memory)达到最大内存后的,对被挑选出来的数据进行删除的策略,如下图:

四、实践建议:如何选择删除策略?
- 优先关闭默认的noeviction策略:生产环境若用 Redis 做缓存,noeviction会导致内存满时写请求失败,建议替换为allkeys-lru或allkeys-lfu;
- 根据业务场景选择淘汰算法:
- 通用缓存(如商品、页面缓存):选allkeys-lru(简单且有效);
- 访问频率波动大的场景(如内容推荐、促销活动):选allkeys-lfu(更精准);
- 有明确过期时间的场景(如会话、短期活动):选volatile-ttl或volatile-lfu;
- 合理配置maxmemory和hz:
- maxmemory:建议设置为物理内存的 50%-70%(避免 Redis 占用过多内存导致系统 OOM);
- hz:默认 10 即可,若内存压力大,可适当提高到 20(增加定期扫描频率),但不建议超过 100(避免 CPU 开销过高);
- 避免大量设置短期过期键:若需频繁清理短期数据,优先用volatile-ttl策略,而非依赖定时删除。
总结
Redis 的删除策略是 “主动清理(定期删除)+ 被动兜底(惰性删除)+ 内存溢出保护(内存淘汰)” 的三层架构,核心目标是平衡 CPU 资源、内存资源与数据一致性。理解每种策略的适用场景,结合业务需求选择合适的内存淘汰策略,是保障 Redis 高性能、高可用的关键。
到此这篇关于Redis中删除策略的几种实现方式的文章就介绍到这了,更多相关Redis 删除策略内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
