Redis 的数据过期策略,到底是怎么删数据的?

0 阅读4分钟

给 Redis 的 key 设了过期时间,时间一到,这条数据会瞬间从内存里消失吗?

这个问题问的其实就是 Redis 的数据过期策略

说白了,当我们给一个 key 指定了 TTL(比如 set token abc123 10,代表 10 秒后失效),Redis 并不会保证在 TTL 到期的瞬间立即删除,而是通过过期策略在合适的时机清理。

这些"什么时候删、怎么删"的规则,就是过期策略。

Redis 一共准备了两套方案:惰性删除定期删除

先说惰性删除

它的思路特别省事:

你设了过期时间之后,Redis 平时压根不搭理它,直到你哪天来读这个 key 了,它才顺手查一下"过期没"。

过期了就当场删掉,没过期就把数据正常返回给你。

举个例子,set name 张三 10,10 秒后你去 get name,Redis 会先判断这个 name 是不是已经到期。

到期了,直接清掉;没到期,返回张三。

整个过程就这么简单。

惰性删除最大的好处是对 CPU 极其友好——只在真正用到 key 的时候才做过期检查,那些压根没人访问的 key,它绝不多花一分力气去扫。

但代价也明显:对内存不友好。

一个 key 过期之后如果一直没人碰,它就会赖在内存里不走,内存永远腾不出来。

要是大量 key 都过期了却没人访问,这些垃圾数据就一直占着地方, 会造成额外的内存占用。

那怎么办?

这就轮到定期删除登场了。

它的逻辑是:

Redis 每隔一段时间,就主动挑一批 key 出来检查,发现过期的就顺手删了。

注意,它每次只查"一定量"的 key,但Redis 会周期性从设置过期时间的 key 中随机抽取一部分进行检查。

随着定期抽查不断进行,大部分过期 key 都会被及时清理,但 Redis 并不会一次性扫描全部 key。

定期删除内部又分为两类执行方式:slow 模式和 fast 模式。

slow 模式默认按照 hz 配置运行,默认 hz=10,也就是每秒执行约 10 次。

这个频率可以通过配置文件改,默认值就是 10。

hz 10

但它还有条硬约束:

单次清理耗时不能超过 25 毫秒

为什么卡得这么死?

因为清理动作会占用主进程,时间太长就会拖累正常请求,所以必须压在 25 毫秒以内。

一次没清完也不要紧,下一轮接着来。

fast 模式更狠,耗时更短。

它的执行频率不固定,执行更加频繁,但 Redis 会限制两次 fast 清理之间的最小间隔,避免过度占用 CPU。

同样是为了尽量少打扰主进程。

把定期删除整体看,它的优点有两条:

第一,通过限制删除操作的时长和频率,把对 CPU 的影响压到最低;

第二,能真正把过期 key 占着的内存释放出来。

缺点则是删除的时长和频率很难拿捏——太频繁就拖累 CPU,太稀疏又退回惰性删除的老毛病,过期数据释放不及时。

那 Redis 到底用了哪一套?

答案是两套一起上

惰性删除和定期删除它全都要,小孩子才做选择,两个配合着用,才能最大程度把过期的 key 清干净。

小贴士

另外需要注意,过期策略解决的是“过期 key 怎么清理”,但如果 Redis 内存达到限制,还有另一套机制——内存淘汰策略

比如 maxmemory-policy 配置,可以选择淘汰一些 key,例如 LRU、LFU 等。

最后收个尾。

过期策略就两种:

惰性删除,访问 key 时才判断过期与否,过期就删;

定期删除,周期性抽查一批 key,过期就删,里面又分 slow 模式(定时任务,默认 10 赫兹,每次清理不超 25 毫秒)和fast 模式(执行更加频繁,但 Redis 会限制执行间隔和单次耗时)。

记住最关键的一点:

Redis 是惰性删除和定期删除配合使用的

如果面试问 Redis 为什么不用单纯的定期删除?

答案就是:全量扫描成本太高,会影响性能。