Redis数据删除与淘汰策略:从原理到实战
Redis采用惰性删除、定期删除及主动删除协同清理过期数据。内存达上限时,触发淘汰策略(如allkeys-lfu、volatile-lfu)删除非过期key。生产需配置maxmemory和maxmemory-policy,避免内存溢出或数据丢失。
Redis 以高性能闻名,所有数据都在内存中运行。但内存资源有限,用一分少一分。因此核心问题随之而来:过期数据如何删除?内存满载后新数据如何存放?

这两个问题分别对应 Redis 的数据删除策略和数据淘汰策略。一个负责清理过期数据,一个负责在内存不足时腾出空间。两者配合决定了 Redis 的性能和内存利用率。虽然听起来简单,但实际工作中不少人容易混淆这两个概念。一旦配置混乱,就可能引发内存溢出、数据丢失或性能问题。
本文将从基础概念铺垫、策略细节拆解、底层原理到实战配置逐一讲解。无论是面试突击还是生产排障,都能直接使用。
一、前置基础:先搞懂两个核心前提
在讲解具体策略之前,需要先明确两个基础概念,否则后续内容容易混淆。
1. 过期数据 vs 非过期数据
Redis 允许通过 EXPIRE、PEXPIRE 等命令为 key 设置过期时间。过期数据是指已到达预设时间但尚未被删除的 key;非过期数据则是指未设置过期时间,或者尚未到达过期时间的 key。
关键点在于:过期不代表立即删除。Redis 不会实时监控每个 key 的过期状态,否则会大量消耗 CPU 资源。它采用“懒删除”或“批量删除”的思路。
2. 数据删除 vs 数据淘汰
这两个概念最容易混淆,用一句话概括:
- 数据删除:针对“过期数据”的清理,核心是删除过期的 key,释放内存。
- 数据淘汰:针对“内存满载”后的处理,核心是删除部分非过期的 key,为新数据腾出空间(仅在内存达到
maxmemory阈值时触发)。
简而言之:删除是清理过期垃圾,淘汰是内存不足时被迫腾空间。
二、Redis 数据删除策略(清理过期数据)
Redis 并未采用“实时删除”机制,即每个 key 过期后立即删除。原因很简单:实时删除会导致 CPU 利用率飙升,尤其是在过期 key 数量较多时,会严重影响 Redis 的核心读写性能。
因此,Redis 采用了三种删除策略结合的方式,在 CPU 性能和内存利用率之间寻找平衡:惰性删除、定期删除、主动删除。
1. 惰性删除(Lazy Expiration):最“懒”的删除,按需触发
核心逻辑
Redis 不会主动监控 key 的过期时间。只有当用户主动访问某个 key 时,才会顺便检查它是否过期:
- 如果未过期,正常返回数据。
- 如果已过期,直接删除并返回 null(或提示该 key 不存在)。
优点
节省 CPU 资源:仅在访问时检查,不额外占用 CPU 时间,对核心读写性能几乎无影响。
缺点
容易浪费内存:如果某个过期 key 一直未被访问,就会一直占用内存。极端情况下,大量过期 key 堆积会迅速耗尽内存,这也是必须配合其他删除策略的原因。
典型场景:假设设置了 100 万个 key,过期时间均为 1 小时。1 小时后所有 key 过期,但只要无人访问,这些 key 就会一直占用内存,直到定期删除或主动删除来处理。
2. 定期删除(Periodic Expiration):折中方案,批量检查
核心逻辑
Redis 每隔一段时间(默认 100 毫秒,可配置),随机抽取一批过期 key 进行检查,发现过期则删除。具体流程如下:
- 每次执行定期删除时,从过期字典(存储所有设置了过期时间的 key)中随机抽取 N 个 key;
- 检查这些 key 是否过期,并删除所有过期的 key;
- 如果删除的 key 数量超过 N 的 25%,说明当前过期 key 较多,则继续执行新一轮检查;否则本次定期删除结束。
关键配置
定期删除的频率由配置项 hz 控制(默认 hz=10)。hz 表示 Redis 每秒执行定期任务的次数:
hz越大,定期删除越频繁,过期 key 清理越及时,但 CPU 消耗也越高。hz越小,CPU 节省越多,但过期 key 清理越慢,内存浪费更严重。
生产环境建议保持默认值 10。如果内存压力较大,可适当调至 20,但需关注 CPU 负载。
优点
在 CPU 和内存之间取得平衡:既不像惰性删除那样过度浪费内存,也不像实时删除那样导致 CPU 过载。
缺点
存在“漏删”可能:由于是随机抽样,某些过期 key 可能一直未被抽到,从而持续占用内存。不过惰性删除可起到补充作用:一旦有人访问该 key,就会被删除。
3. 主动删除(Active Expiration):内存不足时的强制清理
核心逻辑
当 Redis 内存使用量达到 maxmemory 阈值时,会触发“主动删除”:先清理所有过期的 key,释放内存,防止内存溢出。
需要注意的是:主动删除是“淘汰策略的前置步骤”。内存满载时,Redis 会先尝试删除所有过期 key;如果清理后内存仍然不足,才会启动“数据淘汰策略”(即删除非过期的 key)。
触发条件
仅在内存使用量 ≥ maxmemory 时触发。如果内存未达到阈值,即使过期 key 堆积严重,也只会依赖惰性删除和定期删除处理。
总结:三种删除策略的协同工作
Redis 并非依赖单一策略,而是三管齐下:
- 日常依靠“惰性删除”保证 CPU 高效运行,减少无效操作。
- 定期通过“定期删除”批量扫描,降低内存浪费。
- 内存紧张时由“主动删除”先行清理过期 key,腾出空间。
- 若清理过期 key 后依然不足,则进入“数据淘汰策略”阶段。
三、Redis 数据淘汰策略(内存满时腾空间)
当 Redis 内存使用量达到 maxmemory,且过期 key 全部删除后内存仍然不足时,就需要启动“数据淘汰策略”——删除部分非过期的 key,为新数据腾出空间。
Redis 提供 8 种淘汰策略(Redis 6.0+ 版本),常用策略有 6 种,大致可分为三类:按访问频率淘汰、按时间淘汰、按随机淘汰。
先明确:淘汰策略的核心前提
- 淘汰范围:默认仅淘汰“设置了过期时间的 key”。如果设置了过期时间的 key 全部淘汰后仍不够,则会淘汰“未设置过期时间的 key”(除非配置为仅淘汰过期 key)。
- 触发时机:每次执行“写操作”(如
set、hset、lpush等)时,都会检查内存是否达到maxmemory。如果达到,则执行淘汰策略,直到内存降至阈值以下,再执行写操作。 - 核心配置:通过
maxmemory-policy配置淘汰策略(默认值为noeviction)。
8种淘汰策略详解(按常用程度排序)
首先通过对照表概览所有策略,后续再详细拆解常用策略:
| 淘汰策略(配置值) | 核心逻辑 | 适用场景 | 是否常用 |
|---|---|---|---|
| allkeys-lru | 淘汰所有 key 中“最近最少使用”的 key(LRU:Least Recently Used) | 通用场景,大多数业务(如缓存、会话存储) | 非常常用 |
| volatile-lru | 仅淘汰“设置了过期时间”的 key 中,最近最少使用的 key | 需要保留未过期 key(如核心配置),只淘汰临时缓存 | 常用 |
| allkeys-random | 随机淘汰所有 key 中的任意一个 | 对 key 访问频率无要求,追求简单高效 | 不常用 |
| volatile-random | 仅随机淘汰“设置了过期时间”的 key | 同上,且需要保留未过期 key | 不常用 |
| volatile-ttl | 仅淘汰“设置了过期时间”的 key 中,剩余过期时间最短(ttl 最小)的 key | 希望尽快淘汰即将过期的临时缓存 | 场景化常用 |
| volatile-lfu | 仅淘汰“设置了过期时间”的 key 中,最不经常使用的 key(LFU:Least Frequently Used) | key 访问频率差异大,优先保留高频访问 key | 常用(Redis 4.0+ 支持) |
| allkeys-lfu | 淘汰所有 key 中,最不经常使用的 key | 通用场景,比 LRU 更精准(优先淘汰低频访问 key) | 常用(Redis 4.0+ 支持) |
| noeviction(默认) | 不淘汰任何 key,拒绝所有写操作,返回错误(读操作正常) | 不允许数据丢失的场景(如核心业务数据存储) | 特殊场景使用 |
重点拆解:常用策略的核心区别
日常开发中,最常用的是 allkeys-lru、volatile-lru、allkeys-lfu、volatile-lfu 这四种。它们之间的核心区别在于“淘汰范围”和“淘汰依据”:
1. LRU vs LFU:淘汰依据的区别
- LRU(最近最少使用):关注“最后一次访问时间”——长期未被访问的 key 优先淘汰。
- LFU(最不经常使用):关注“一段时间内的访问次数”——访问频率最低的 key 优先淘汰。
举例说明:
- keyA:每天访问 1 次,但每次访问都在最近 1 小时内。
- keyB:每天访问 100 次,但最后一次访问在 3 天前。
LRU 会淘汰 keyB(因为近期最少访问),LFU 会淘汰 keyA(因为访问频率最低)。因此 LFU 更适合“访问频率差异大”的场景,能够更好地保留高频访问的核心缓存。
2. allkeys vs volatile:淘汰范围的区别
- allkeys-*:范围涵盖“所有 key”(无论是否设置过期时间),适用于“所有数据均为缓存”的场景(如商品缓存、接口缓存)。
- volatile-*:范围仅限于“设置了过期时间的 key”,适用于既有核心数据(未设置过期时间)又有临时缓存(设置了过期时间)的场景(例如核心配置 key 不设过期,临时会话 key 设过期)。
淘汰策略的底层实现(简化版)
很多人关心 Redis 如何记录每个 key 的访问时间或频率,并据此实现 LRU/LFU 淘汰。以下为简化原理(无需深入源码):
- 对于 LRU:Redis 为每个 key 维护一个“最后访问时间戳”。淘汰时,在候选 key 中遍历,选择时间戳最小的(即最近最少访问的)进行删除。
- 对于 LFU:Redis 为每个 key 维护一个“访问计数器”。每次访问 key,计数器加一;每隔一段时间计数器会衰减,避免长期高频率的 key 永远占据位置。淘汰时,选择计数器最小的(最不经常使用的)进行删除。
注意:Redis 的 LRU/LFU 并非严格的“全量排序”(全量排序会消耗大量 CPU),而是通过“采样排序”实现。每次淘汰时,随机抽取一定数量的 key(默认 5 个,可通过 maxmemory-samples 调整),在抽到的 key 中选出最应淘汰的那个,从而兼顾性能和准确性。
四、核心配置实战(生产环境必看)
理解删除和淘汰策略后,最关键的是“生产环境如何配置”,以避免内存溢出或数据异常。以下为核心配置项及推荐值:
1. 核心配置项说明
| 配置项 | 作用 | 推荐值 |
|---|---|---|
| maxmemory | 设置 Redis 最大可用内存(必须配置,否则内存无限增长,可能导致服务器崩溃) | 根据服务器内存配置,建议设为服务器内存的 50%-70%(例如 8G 内存设为 4G) |
| maxmemory-policy | 设置数据淘汰策略 | 通用场景:allkeys-lfu;存在核心未过期 key:volatile-lfu |
| hz | 定期删除的频率(每秒执行次数) | 默认 10,内存压力大时可调至 20 |
| maxmemory-samples | LRU/LFU 淘汰时的采样数量 | 默认 5,采样越多淘汰越准,但 CPU 消耗越高(建议保持默认) |
2. 推荐配置示例(redis.conf)
# 设置最大内存为 4G maxmemory 4gb # 淘汰策略:所有 key 中最不经常使用的 maxmemory-policy allkeys-lfu # 定期删除频率:每秒 10 次 hz 10 # LFU 采样数量:5 个 maxmemory-samples 5
3. 动态调整配置(无需重启 Redis)
生产环境中如需临时修改配置,可通过 Redis 命令动态调整(重启后失效,需同步修改 redis.conf 才能永久生效):
# 动态设置最大内存为 4G config set maxmemory 4gb # 动态设置淘汰策略为 allkeys-lfu config set maxmemory-policy allkeys-lfu # 查看当前所有配置 config get *
五、常见问题与避坑指南
在实际使用 Redis 数据删除和淘汰策略时,常见以下问题。提前了解可避免很多麻烦:
1. 问题1:内存溢出,Redis 拒绝写操作(返回 OOM command not allowed when used memory > 'maxmemory')
原因:淘汰策略使用了默认的 noeviction,内存满载时不会淘汰任何 key,直接拒绝所有写操作。
解决方案:将淘汰策略改为 allkeys-lfu 或 volatile-lfu,同时检查 maxmemory 配置是否合理。
2. 问题2:大量过期 key 堆积,内存占用过高
原因:定期删除频率过低(hz 太小),且过期 key 长期无人访问(惰性删除无法触发)。
解决方案:适当调高 hz 值(例如调至 20),同时在业务层添加定时任务,定期清理过期 key(例如使用 DEL 命令扫描删除)。
3. 问题3:核心 key 被淘汰
原因:使用了 allkeys-* 策略,核心 key 未设置过期时间,内存满载时也被淘汰;或者核心 key 设了过期时间,被 volatile-* 策略删除。
解决方案:核心 key 不应设置过期时间,淘汰策略选用 volatile-lfu(仅淘汰设置了过期时间的临时 key)。
4. 问题4:LRU 淘汰策略不精准
原因:maxmemory-samples 采样数量过少,抽到的“最近最少使用”key 可能并非真正的低频 key。
解决方案:适当提高 maxmemoy-samples(例如调至 10),但需关注 CPU 负载。
六、总结
Redis 数据删除与淘汰策略,核心在于平衡 CPU 性能、内存利用率和数据一致性三方面。牢记以下几点,生产场景基本不会出现问题:
- “删除策略”负责清理过期 key,三种策略协同工作:惰性删除(节省 CPU)+ 定期删除(折中方案)+ 主动删除(内存不足时的最后防线)。
- “淘汰策略”负责内存满载时腾出空间,常用
allkeys-lfu(通用场景)和volatile-lfu(存在核心 key 的场景)。 - 生产环境必须配置
maxmemory和maxmemory-policy,否则内存迟早溢出。 - LRU 关注“最近访问时间”,LFU 关注“访问频率”,根据业务场景选择。
- 核心 key 不应设置过期时间,从而避免在淘汰时被误删。
掌握这些策略,不仅能有效解决生产环境的内存问题,也能在面试中从容应对——Redis 淘汰策略是后端面试的高频考点。
