Redis 里的 key 可以设置过期时间,但 key 到期并不意味着会被立刻删除。Redis 需要在 CPU 成本和内存释放之间做平衡,所以采用的是:惰性删除 + 定期删除。
当内存真的不够用时,Redis 还会根据内存淘汰策略删除一部分 key。
这篇把两个问题放在一起讲:
- key 过期后怎么删除?
- 内存满了以后怎么淘汰?
一、惰性删除:访问时才检查
惰性删除的意思是:设置了过期时间后,Redis 不会一直盯着这个 key。只有当客户端访问这个 key 时,Redis 才检查它是否过期。
未过期
已过期
客户端访问 key
key 是否过期
返回 value
删除 key
返回空
优点是对 CPU 友好,因为 Redis 不需要为每个过期 key 立刻做检查。
缺点是对内存不友好:如果一个 key 已经过期,但再也没人访问它,它可能会继续占在内存里。
二、定期删除:周期性抽样清理
为了避免过期 key 一直占内存,Redis 会定期抽取一部分 key 检查并删除过期数据。
是
否
定时任务
随机抽取部分 key
是否过期
删除
保留
课件里提到定期清理有两种模式:
| 模式 | 特点 |
|---|---|
| SLOW | 定时任务,默认 10hz,每次不超过 25ms |
| FAST | 执行频率不固定,两次间隔不低于 2ms,每次不超过 1ms |
定期删除的优点是可以主动释放内存,同时通过限制时长和频率减少对 CPU 的影响。缺点是很难精确控制执行频率:太频繁会影响 CPU,太少又会让过期 key 占用内存。
所以 Redis 最终采用组合策略:
text
Redis 过期删除策略 = 惰性删除 + 定期删除
三、内存满了怎么办?
过期删除解决的是"已经设置 TTL 的 key 到期后怎么清理"。但如果 Redis 内存已经满了,新的写入进来怎么办?
这就涉及内存淘汰策略。Redis 会按照配置的规则选择一部分 key 删除。
四、Redis 的 8 种淘汰策略
| 策略 | 作用范围 | 淘汰规则 |
|---|---|---|
| noeviction | 不淘汰 | 内存不足时新写入直接报错 |
| volatile-ttl | 设置了 TTL 的 key | 剩余 TTL 越小越先淘汰 |
| allkeys-random | 全体 key | 随机淘汰 |
| volatile-random | 设置了 TTL 的 key | 随机淘汰 |
| allkeys-lru | 全体 key | 淘汰最近最少使用 |
| volatile-lru | 设置了 TTL 的 key | 淘汰最近最少使用 |
| allkeys-lfu | 全体 key | 淘汰访问频率最低 |
| volatile-lfu | 设置了 TTL 的 key | 淘汰访问频率最低 |
默认策略是 noeviction,也就是内存不足时不删除任何数据,新写入会报错。
五、LRU 和 LFU 的区别
LRU 是 Least Recently Used,最近最少使用。它看的是"多久没被访问过"。
text
当前时间 - 最后一次访问时间 = 越大越优先淘汰
LFU 是 Least Frequently Used,最少频率使用。它看的是"访问次数多不多"。
text
访问频率越低,淘汰优先级越高
简单理解:LRU 更关注最近有没有用,LFU 更关注长期用得多不多。
六、开发中怎么选?
| 业务特点 | 推荐策略 |
|---|---|
| 有明显冷热数据 | allkeys-lru |
| 访问频率差别不大 | allkeys-random |
| 有置顶数据不能淘汰 | volatile-lru,置顶 key 不设置 TTL |
| 短时高频访问明显 | allkeys-lfu 或 volatile-lfu |
| 不允许 Redis 自动删数据 | noeviction |
面试里经常会问:"数据库有 1000 万数据,Redis 只能缓存 20 万,怎么保证 Redis 里都是热点数据?"
可以回答:使用 allkeys-lru 或 allkeys-lfu,让 Redis 自动淘汰不常访问的数据,留下更热的数据。
最后总结一句:
Redis key 过期不会立刻删除,而是惰性删除和定期删除配合;内存不够时才会触发淘汰策略。平时开发最常用的是 allkeys-lru,能让最近常访问的数据尽量留在缓存里。