缓存失效策略怎么选:TTL、主动删除与延迟双删的取舍

举报
云上小栈 发表于 2026/10/29 09:20:25 2026/10/29
【摘要】 三种失效方式的分工缓存失效只有三条路:等过期(TTL)、写操作时删缓存、把新值写回缓存。选型不是三选一,而是明确每种数据走哪条路。经验规则是:读多写少、允许短暂陈旧的配置类数据走 TTL;要求读后立即可见的数据走主动删除;写入频繁且读很稀疏的数据不要缓存。下面按实际配置和命令说明各自适用条件。TTL 到期:粒度粗但最稳TTL 只保证“最终一致”,过期时间要按数据的可容忍陈旧时间设。比如商品库...

三种失效方式的分工

缓存失效只有三条路:等过期(TTL)、写操作时删缓存、把新值写回缓存。选型不是三选一,而是明确每种数据走哪条路。经验规则是:读多写少、允许短暂陈旧的配置类数据走 TTL;要求读后立即可见的数据走主动删除;写入频繁且读很稀疏的数据不要缓存。

下面按实际配置和命令说明各自适用条件。

TTL 到期:粒度粗但最稳

TTL 只保证“最终一致”,过期时间要按数据的可容忍陈旧时间设。比如商品库存可容忍 30 秒,就设 EX 30。对一批同类 key 设置固定的过期时间,则会出现同时过期、请求集中落库的情况。

SET goods:sku:1001 '{"stock":12}' EX 30
CONFIG SET maxmemory 4gb
CONFIG SET maxmemory-policy allkeys-lfu

关键点是过期不能只靠 maxmemory-policy 代替,淘汰策略是内存压力下的兜底,不是失效手段。把 maxmemory 留出 25% 余量,避免写入时频繁触发淘汰。

主动删除:先更新库再删缓存

顺序必须是先更新数据库、后删除缓存。反过来写会在“删除后、更新前”之间留下旧值,且这个旧值直到 TTL 到期都不会被修正。删除用 DEL,不要用 SET 写空值,除非要防穿透并且能给空值一个很短的 TTL(如 60 秒)。

UPDATE t_order SET status = 2 WHERE id = 1001;
DEL order:1001

删除失败时要重试。简单做法是把失败的 key 投进消息队列,由消费者重删,重试 3 次仍失败则告警并写入待清理表。

踩过的坑一:并发读写导致旧值回填

现象:订单状态更新完成后,前端偶发读到旧状态,持续约 10 分钟。原因是读线程在删除前已经把旧值读出来,删除后它再把旧值写回缓存。

解决办法是延迟双删:删一次,等一个略大于“一次读请求写缓存耗时”的间隔(实测 500 毫秒足够),再删一次,把窗口内回填的旧值清掉。

DEL order:1001
// 业务线程或延迟队列,500ms 后
DEL order:1001

更彻底的做法是给缓存值带上版本号,回填时用 Lua 做比较写入,版本旧的直接丢弃:

local cur = redis.call('GET', KEYS[1])
if cur and tonumber(cur) >= tonumber(ARGV[1]) then return 0 end
redis.call('SET', KEYS[1], ARGV[1], 'EX', ARGV[2])
return 1

踩过的坑二:大量 key 同时过期打穿数据库

现象:整点刷新的 2 万条商品 key 都设了 3600 秒 TTL,每到整点数据库 QPS 从 800 涨到 6000,接口 P99 从 40ms 升到 700ms。

解决办法有两步。一是给 TTL 加随机抖动,把 3600 改成 3600 + rand(0, 600),把过期点摊到 10 分钟内。二是对热点 key 用逻辑过期:值里带 expireAt,过期后由单个线程异步重建,其余请求先返回旧值,重建期间用 SET NX 加锁保证只有一个请求回源。

SET lock:rebuild:goods:1001 1 NX EX 10

加锁失败说明已有线程在重建,直接读旧值或短暂重试 50 毫秒即可,不要所有请求一起查库。

小结

允许陈旧的数据用 TTL,且必须加随机抖动;需要读后立即可见的用“先更库、后删缓存”,删除失败进队列重试;并发场景补上延迟双删或用版本号比较写入;热点 key 用逻辑过期加单线程重建。淘汰策略只做内存兜底,不要拿它当失效方案。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。