给 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 为什么不用单纯的定期删除?
答案就是:全量扫描成本太高,会影响性能。