
三个问题对应三种成因
穿透是查询根本不存在的数据,缓存永远不命中,请求全部落到数据库。常见于被构造的非法标识,或者参数解析后出现偏差。
击穿是某个热点键恰好过期,瞬间大量请求同时回源。区别于穿透的地方在于数据是存在的,只是那一刻缓存里没有。
雪崩是大批键在同一时间集中失效,或者缓存服务整体不可用,导致数据库压力骤增。三者的处理手段有重叠,但切入点不同。
穿透的拦截思路
对查询结果为空的键也写一份短过期时间的空值占位,避免同样的非法参数反复打到数据库。过期时间不宜太长,防止数据后来真实存在了却一直读到空。
在入口做参数校验,格式不合法或超出范围的直接返回,不要进入缓存与数据库链路。这是成本最低的一层。对空值占位的键要单独统计数量,异常增长就说明有人在扫描不存在的标识。缓存键的命名要能按业务前缀批量失效,避免只能全量清空。
数据量可控且需要频繁判断存在性的场景,用位图过滤器先挡一轮。它有误判为存在的可能,所以只能作为前置过滤,不能作为最终判定。
击穿的应对
热点数据的过期时间可以设得长一些,并配合后台任务主动刷新,让缓存始终有值。牺牲一点实时性换取数据库的稳定。
对同一个键的回源加互斥锁,只有一个请求去查库并回填,其余请求短暂等待后重试读取。锁要有超时,避免持有者异常导致阻塞。
逻辑过期是另一种思路:值里带一个逻辑过期时间,物理上不过期,发现逻辑过期后由一个线程异步重建,其他请求先返回旧值。
雪崩的预防
过期时间加随机扰动是最简单的做法,把集中失效摊开到一个区间内。基础时长加上一个随机偏移即可。
缓存层做多级或者高可用部署,避免单点故障导致整体不可用。客户端要配好超时与重试,并且限制重试次数,防止故障时重试风暴把后端压垮。
关键链路要有降级方案。缓存不可用时返回兜底数据或者直接拒绝部分非核心请求,保住核心业务。降级的开关要能随时手动调整。
一致性与运行监控
更新数据时先更新数据库再删除缓存,是更常用的顺序。删除比更新缓存更安全,能避免并发写导致的中间态。删除失败要有重试队列或者依赖过期时间兜底。
缓存里的值可以带上版本或更新时间,读出来时判断是否已陈旧,便于做后续的校验和补偿。
命中率、回源数量、响应时间、连接数是最该盯的几个指标。命中率突然下降通常意味着有异常键或者数据被批量清理。给回源请求量设阈值告警,出现陡增就说明缓存层出了问题。
缓存容量要留有余量,接近内存上限时淘汰会变得频繁,命中率随之下降,形成恶性循环。


