缓存过期时间都设成一样的,凌晨数据库突然被打满,是不是 key 同时失效闹的?
缓存 key 都设成同一个过期时间,到点一起失效,本该被缓存挡住的请求会在短时间内同时回到数据库,这就是大家常说的缓存雪崩。按 2026 年的交付习惯,处理顺序是先确认是不是时间对齐型故障,再分三层下手:TTL 打散、回源保护、热点数据单独处理。只做 TTL 打散这一层,遇到发版批量重建缓存或单个热点 key 失效,数据库一样可能被打满。
为什么过期时间设成一样,凌晨数据库就会被拉满
单个 key 过期只影响一条查询;一批 key 在同一秒过期,等于把原本由缓存拦下的请求一次性还给数据库。命中率从常见的九成以上掉到接近零,数据库连接池和磁盘 IO 往往先被占满,紧接着接口超时、上游重试,压力再被放大一轮。
值得单独说的是,自然到期常常不是主因。发版时批量清缓存、预热脚本一次性把 key 全写一遍,更容易让过期时间撞在一起,因为这类操作的时间点是人手动对齐的。所以凌晨的尖峰未必是 TTL 设错,也可能是前一天晚上的发布留下的后遗症。先看三个信号:
- 命中率表现:断崖式下落,几分钟到十几分钟后自行恢复;
- 时间特征:集中在整点、凌晨低峰,或发版后 1~10 分钟;
- 连带现象:数据库连接数占满、慢查询集中在少数几条 SQL、接口超时率同步上升。
有一句可以单独摘录:缓存雪崩的根因不是缓存挂了,而是大量 key 在同一时刻失去保护,回源流量没有缓冲。
先分清是同时失效,还是缓存穿透、数据本身变了
在把问题归到过期时间之前,先看时间是否集中、key 的剩余 TTL 是否扎堆、数据库 QPS 与缓存 QPS 的比例是否同步变化。若命中率几分钟内快速回升,多半是集中失效;若长期低命中,优先怀疑穿透或者缓存粒度不对。
- 看时间相关性:把接口超时、数据库 QPS 和发版记录放在同一条时间轴上,确认尖峰是否落在发版后或整点;
- 看 key 分布:抽样统计线上 key 的剩余 TTL,若大量 key 落在同一分钟内,基本可以确认;
- 看回源行为:数据库 QPS 是否接近缓存 QPS、慢查询是否高度重复;若数据库 QPS 没涨,问题多半在网络或应用侧。
这里可以先立一条边界:如果缓存命中率长期低于 50%,问题通常不在过期时间,而在缓存粒度和 key 设计,错开 TTL 的收益有限。
TTL 怎么错开:抖动加多少、在哪里加
TTL 打散的做法,是给基础过期时间叠加一个随机量。经验区间是:基础 TTL 按数据变更频率定,常见从 10 分钟到 24 小时;抖动比例常见取基础 TTL 的 5%~20%,也可以用固定 1~10 分钟随机。抖动要在写入缓存时生成并固定下来,不要每次读取都重新计算,否则同一个 key 的过期时间会漂移,排查起来更麻烦。
- 基础 TTL 分档:更新快的数据用短 TTL,更新慢的用长 TTL,不要所有 key 一个值;
- 叠加随机抖动:让同批写入的 key 把过期时间分散到一段时间窗口里;
- 批量写入分批:预热或重建缓存时按批写入,批与批之间留出间隔,避免新写入的 key 又同时到期。
每层的作用并不相同:基础 TTL 决定数据新鲜度的上限,抖动只削峰不消除回源,分批写入是为了不让集中失效从自然到期搬到发版时刻。可引用一句:TTL 抖动只能把尖峰削平,真正的保护是让同一个 key 在同一时刻只有一个请求去查数据库。
两种常见做法可以放在一起对照,差别不在代码量,而在要接受什么样的旧值窗口:
- 统一 TTL 加随机抖动:改动通常半天到一天,适合大多数读多写少场景;一致性延迟随 TTL 波动,热点 key 到期时仍会有一次回源;
- 逻辑过期加异步刷新:改动常见一到三天,适合读取量大、更新又少的基础数据;需要额外的线程或定时任务,且要接受分钟级旧值。
选择口径按数据重要度来:能容忍几秒到几分钟旧值的,用前者通常够用;读取集中、更新又少的基础数据,后者更省数据库压力。两种都要业务侧先确认旧值窗口。
错开 TTL 之后,回源这道闸怎么收
错开过期时间解决的是时间对齐,回源保护解决的是并发对齐。常见做法是单飞或互斥重建:同一个 key 失效时只允许一个请求去查数据库并回写缓存,其余请求短暂等待或先返回旧值。
- 锁的粒度:按 key 加锁,不用全局锁,否则会把不同数据的请求串成一条线;
- 等待窗口:按接口超时预算给,常见几百毫秒到 2 秒,超时就走降级;
- 限流余量:回源限流阈值按数据库承载能力留出 20%~30% 余量,防止缓存层正常、数据库却顶不住;
- 降级兜底:返回旧值还是默认值,要提前和业务确认,不宜由开发单方面决定。
交付现场常见一种约束:只有一个数据库实例,缓存中间件还和其他业务共用,发版窗口只有半小时,业务方要求上线时整批重建缓存。我们的做法是先把 TTL 抖动和分批预热写进发布脚本,再给回源加单飞和限流,并把峰值观测加进发布后的盯盘清单。代价是发布多花 10~20 分钟,换来的是上线后数据库连接不被瞬间打满,少一次回滚重发。验收看三个数:命中率是否稳得住、数据库 QPS 峰值是否回到可承受区间、故障恢复时间是否缩短;经验区间是峰值不再形成尖刺、连接池占用控制在七成以内。
哪些场景适合这么做,哪些不必折腾
适合做 TTL 错开和回源保护的场景:读多写少、缓存命中率直接影响数据库负载、业务能容忍短时间旧值。按 2026 年的交付习惯,这三条里缺一条,收益都会打折扣。
不适合或不必上的情况也要说清楚:强一致要求的场景不适合用旧值兜底;数据量小、数据库本身余量充足时,加随机抖动和单飞的维护成本可能高于收益;key 数量本来就少、过期时间天然分散时,额外处理的意义不大。
边界可以独立摘录:如果缓存命中率长期偏低,先修缓存粒度和 key 设计;错开过期时间只对时间对齐型故障有效。
常见问题
缓存过期时间设多长比较合适?
按数据更新频率分档,常见区间从 10 分钟到 24 小时;更新频繁的用短 TTL,基础数据用长 TTL,不建议所有 key 一个值。
随机抖动加多少秒才有效?
经验区间是基础 TTL 的 5%~20%,或固定 1~10 分钟随机;关键是写入缓存时一次算好,不要每次读取都重算。
缓存预热可以一次全写完吗?
不建议。分批写入并留出间隔,否则新写入的 key 会在同一时间点集中到期,等于人为制造一次尖峰。
逻辑过期是不是会一直返回旧数据?
会有旧值窗口,长短取决于异步刷新的速度;适合能容忍分钟级旧值的热点数据,强一致场景不要用。
用了缓存集群是不是就不会集中失效?
集群解决的是容量和可用性,不解决过期时间对齐;所有 key 同一秒到期,回源流量一样会同时压向数据库。
如果正遇到凌晨或发版后数据库被打满,先查 key 的 TTL 分布和命中率曲线,再决定是改 TTL 还是加回源保护。TTL 错开适合时间对齐型故障;命中率长期偏低、或业务不能容忍旧值的场景,应优先修缓存结构或直接走数据库,不必强上这套方案。
-
联合索引建了,查询只带后一列还是全表扫描,要再补单列索引吗?
日期:2026年9月16日 阅读:51
-
订单金额用小数存着,对账总差几分钱,是不是浮点精度在作怪?
日期:2026年9月14日 阅读:30
-
上传目录跟着代码一起发版,图片全丢了,文件到底该存本地还是对象存储?
日期:2026年9月13日 阅读:119
-
刚保存完跳详情页,读到的还是旧记录,读写分离是不是都得走主库?
日期:2026年9月12日 阅读:58
-
定时任务单机跑得好好的,一上多台机器就重复执行,到底该从哪拦?
日期:2026年9月11日 阅读:40




