Redis 作为高性能的内存数据库,所有数据都存储在内存中,而内存资源是有限的------这就引出了两个核心问题:过期数据如何删除?内存满了之后新数据如何存储?
这两个问题分别对应 Redis 的「数据删除策略」和「数据淘汰策略」,二者相辅相成,共同决定了 Redis 的性能、内存利用率和数据一致性。很多开发者在使用 Redis 时,容易混淆这两个概念,甚至因配置不当导致内存溢出、数据丢失或性能瓶颈。
本篇博客将从「基础概念铺垫」到「策略细节拆解」,再到「底层原理」和「实战配置」,把 Redis 数据删除与淘汰策略讲透,无论是面试备考还是生产实践,都能直接套用。
一、前置基础:先搞懂2个核心前提
在讲解具体策略前,必须先明确两个基础概念,否则容易理解偏差:
1. 过期数据 vs 非过期数据
Redis 支持给 key 设置过期时间(通过 EXPIRE、PEXPIRE 等命令),过期数据是指「到达预设过期时间但尚未被删除的 key」;非过期数据则是没有设置过期时间,或未到达过期时间的 key。
注意:过期数据不一定会被立即删除,Redis 不会实时监控每一个 key 的过期状态(否则会消耗大量CPU),而是通过特定策略"懒"删除或"批量"删除。
2. 数据删除 vs 数据淘汰
这是最容易混淆的两个概念,一句话区分:
-
数据删除:针对「过期数据」的清理操作,核心是"删除过期的key",释放内存;
-
数据淘汰:针对「内存满了之后」的处理操作,核心是"删除部分非过期数据",为新数据腾出内存(只有当内存达到 maxmemory 阈值时才会触发)。
简单说:删除是"清理过期垃圾",淘汰是"内存不够时腾地方"。
二、Redis 数据删除策略(清理过期数据)
Redis 没有采用"实时删除"(每一个 key 过期就立即删除),因为实时删除会导致 CPU 利用率飙升(尤其是过期 key 极多的场景),影响 Redis 核心读写性能。
实际上,Redis 采用「三种删除策略结合」的方式,兼顾 CPU 性能和内存利用率,分别是:惰性删除、定期删除、主动删除。
1. 惰性删除(Lazy Expiration):最"懒"的删除,按需触发
核心逻辑
Redis 不主动监控 key 的过期时间,只有当「用户主动访问某个 key」时,才会检查该 key 是否过期:
-
如果 key 未过期:正常返回数据;
-
如果 key 已过期:立即删除该 key,返回 null(或不存在)。
优点
极致节省 CPU 资源:只在"访问时"才检查过期,不占用额外的 CPU 时间,对 Redis 核心读写性能几乎无影响。
缺点
内存浪费严重:如果一个过期 key 长期不被访问,它会一直占用内存,直到被访问时才会被删除。极端情况下,大量过期 key 堆积会导致内存溢出(这也是为什么需要配合其他删除策略)。
举个例子:设置 100 万个 key,过期时间为 1 小时,1 小时后所有 key 都过期,但如果没人访问这些 key,它们会一直占用内存,直到被定期删除或主动删除清理。
2. 定期删除(Periodic Expiration):折中方案,批量检查
核心逻辑
Redis 会每隔一段时间(默认 100ms,可通过配置调整),随机抽取一部分过期 key 进行检查,如果发现过期,就删除它们。具体流程:
-
每次执行定期删除时,从过期字典(存储所有过期 key 的字典)中随机抽取 N 个 key;
-
检查这些 key 是否过期,删除所有过期的 key;
-
如果删除的 key 数量超过 N 的 25%,则重复步骤 1(说明当前过期 key 较多,需要继续清理);否则结束本次定期删除。
关键配置
定期删除的频率由配置 hz 控制(默认 hz=10),hz 的值表示「Redis 每秒执行定期任务的次数」:
-
hz 值越大,定期删除越频繁,过期 key 清理越及时,但 CPU 占用越高;
-
hz 值越小,CPU 占用越低,但过期 key 清理越慢,内存浪费越严重。
生产环境建议:hz 保持默认 10 即可,若内存压力较大,可调整为 20(需注意 CPU 负载)。
优点
折中了 CPU 和内存:既不会像惰性删除那样浪费大量内存,也不会像实时删除那样占用过多 CPU。
缺点
存在"漏删"风险:因为是随机抽取检查,可能有部分过期 key 一直未被抽取到,从而长期占用内存(但可以通过惰性删除弥补:只要用户访问,就会被删除)。
3. 主动删除(Active Expiration):内存不足时的强制清理
核心逻辑
当 Redis 内存使用达到 maxmemory(最大内存阈值)时,会触发「主动删除」------优先删除过期 key,释放内存,避免内存溢出。
注意:主动删除是「淘汰策略的前置步骤」:当内存满时,Redis 会先尝试删除所有过期 key;如果删除完所有过期 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,再执行写操作。
-
核心配置:通过
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(根据淘汰范围),筛选出时间戳最小(最近最少访问)的 key 进行删除。
-
对于 LFU:Redis 给每个 key 维护一个「访问计数器」,每次访问 key 时,计数器加 1;每隔一段时间,计数器会衰减(避免旧的高频 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。
解决方案:适当提高 maxmemory-samples(如调整为 10),但需注意 CPU 负载。
六、总结
Redis 数据删除与淘汰策略,核心是「平衡 CPU 性能、内存利用率和数据一致性」,记住以下核心要点,就能轻松应对生产场景:
-
「删除策略」是清理过期 key,三种结合:惰性删除(省 CPU)+ 定期删除(折中)+ 主动删除(内存不足时);
-
「淘汰策略」是内存满时腾空间,常用 allkeys-lfu(通用)和 volatile-lfu(有核心 key);
-
生产环境必须配置 maxmemory 和 maxmemory-policy,避免内存溢出;
-
LRU 看"最近访问时间",LFU 看"访问频率",根据业务场景选择;
-
核心 key 不设置过期时间,避免被淘汰。
理解了这些策略,不仅能解决 Redis 内存相关的问题,还能在面试中从容应对相关问题(Redis 淘汰策略是后端面试高频题)。
如果需要,我还可以为你补充:
-
LRU/LFU 底层源码简化解析;
-
过期 key 清理的监控方法;
-
不同业务场景(缓存、会话、配置存储)的具体策略配置。