## 缓存击穿/穿透/雪崩的简单总结 - 我对这几个概念不太喜欢,因为名字容易记混、定义的细节也容易混淆。 - 下面按照我的方式做的总结,不再纠结这几个名字,而是重点阐述问题的维度和问题的实质,以便于更好的记忆、区分和应对方案设计。 ### 问题的根本原因 这几个工程问题根本原因的共性:查询缓存的key不存在。 而“key 不存在”可以拆成两个维度来看: 1. **数据本身在数据源里就不存在**:无论查多少次,缓存都不会有,请求每次都会落到数据库(甚至被恶意构造成海量不同的 key)。 2. **数据在数据源里存在,但此刻缓存里没有**:可能是还没加载、也可能是刚好过期失效,请求短时间内集中打到数据库。 围绕这两个维度,再叠加“单个 key”还是“大批量 key”的规模差异,就对应到了那三个被起了名字的场景。 ### 三种场景的实质与区分 | 场景 | 实质 | 规模 | 数据源中是否存在 | | --- | --- | --- | --- | | 穿透 | 查一个数据源里根本不存在的 key,缓存永远挡不住 | 单个/大量 | 不存在 | | 击穿 | 某个热点 key 过期的瞬间,大量并发同时回源 | 单个热点 key | 存在 | | 雪崩 | 大批 key 在同一时间集中失效,或缓存整体不可用 | 大量 key | 存在 | 一句话区分: - **穿透**:数据本来就没有 → 缓存形同虚设。 - **击穿**:数据有,但“一个热点 key”在失效瞬间被并发击垮。 - **雪崩**:数据有,但“一大片 key”同时失效 / 缓存宕机,压力整体压向 DB。 ### 应对方案 **针对穿透(key 对应的数据不存在)** - 缓存空值:查不到也把空结果缓存起来(设置较短过期时间),避免相同的无效 key 反复回源。 - 布隆过滤器:在缓存之前加一层布隆过滤器,快速判断 key 是否“可能存在”,不存在的直接拦掉。 - 接口层校验:对明显非法的 key(格式、范围)提前拦截,防止被恶意构造。 **针对击穿(单个热点 key 失效瞬间的并发回源)** - 互斥锁 / 单飞(singleflight):只放一个请求去回源重建缓存,其余请求等待或短暂重试。 - 逻辑过期:缓存不设物理过期,存一个逻辑过期时间,过期后由后台异步重建,读请求始终拿到旧值不阻塞。 - 热点 key 预热与永不过期:对确定的热点数据提前加载并单独维护刷新。 **针对雪崩(大量 key 同时失效 / 缓存整体不可用)** - 过期时间打散:在基础过期时间上加随机抖动,避免大量 key 同一时刻集中失效。 - 多级缓存:本地缓存 + 分布式缓存,降低单层失效带来的冲击。 - 高可用与限流降级:Redis 集群 / 主从保证可用;DB 前加限流、熔断、降级,避免被瞬时流量打垮。 ### 小结 抓住“查询缓存的 key 不存在”这个共性,再区分**数据源里到底有没有**以及**是单点还是大面积**,三种场景和对应方案就能自然对上号,不必去死记名字。