什么是缓存穿透,如何解决?
未标记
考查重点:不存在键访问与数据库保护。不存在的数据反复穿过缓存查数据库;参数校验、短期空值和布隆过滤分层拦截,注意新数据加入与误判处理
本题目录
参考答案
核心回答
缓存穿透是指请求查询的数据在缓存和数据库中都不存在,未命中结果又没有被有效拦截或缓存,导致相同或大量不存在的键反复查询数据库。它可能来自恶意构造的 ID,也可能来自旧链接、删除后的数据等正常请求。
通常分层治理:
- 入口校验与限流。 拒绝明显无效的参数,限制异常请求及数据库回源量。
- 缓存“不存在”的结果。 数据库明确查无数据后,写入可识别的空值标记并设置较短
TTL,后续相同请求直接返回;标记要与“缓存未命中”区分。 - 用布隆过滤器过滤大量随机键。 将可查询的 ID 加入过滤器,判断确定未收录的键可提前拦截;判断可能存在的键仍正常查缓存和数据库。前提是过滤器已完整构建,并能及时同步新数据。
空值缓存适合重复访问同一个不存在的键;布隆过滤器更适合拦截大量不同的不存在键。两者可结合,但仍需控制回源,避免误判、同步延迟或组件故障把压力全部转给数据库。
空值缓存要处理哪些细节
以商品详情为例:先查商品缓存,命中商品值就返回,命中“不存在”标记就返回未找到;真正未命中时再查数据库,查到商品则写正常缓存,明确没有记录才写短期空值。
TTL 要结合新数据可见性的要求选择。过长会让后来创建的商品继续被当成不存在;过短则增加回源。创建商品提交成功后,应清理相应空值,失败时有重试,TTL 作为兜底。并发中的旧查询仍可能晚于清理写回空值;若业务不接受这段窗口,需要进一步协调回填与创建,不能只依赖一次删除。
数据库超时、连接失败不代表“商品不存在”,不能统一缓存成未找到。可以按业务返回暂时不可用,或单独短期缓存错误状态以抑制重试,但应保留它与业务空结果的区别。
随机键攻击还可能灌入大量空值,占满缓存,因此需要限制空值缓存规模、保留时间和入口流量,而不是无限缓存所有无效 ID。
布隆过滤器为什么会误判
布隆过滤器使用多个哈希函数把 ID 映射到位数组。查询时只要有一位为 0,就能判断该 ID 没被加入;如果对应位都是 1,可能是这个 ID,也可能由其他 ID 的映射共同造成,所以会出现假阳性。
这个性质针对的是已加入过滤器的集合。数据库新增记录尚未同步、过滤器丢失或重建未完成时,数据库中的真实商品也可能未被收录。此时不能把过滤器的负结果直接当成业务不存在,应识别过滤器的就绪状态,并按可用性要求采用受限回源等策略。布隆过滤器本身不提供数据库与过滤器的原子更新。
误判率越低通常需要越多空间或计算;容量增长也要规划。普通布隆过滤器的位可能由多个元素共享,删除商品时不能随便清零对应位,否则可能影响其他 ID;保留这些位会多放行一些查询,可以通过受控重建等方式治理。
追问:加锁或者随机 TTL 能解决穿透吗
同键请求合并或互斥回源可以减少并发查库,但如果每次查不到都不保存结果,下次仍会回源;对大量不同键,逐键加锁也难以解决总体压力。
随机 TTL 主要缓解大量缓存集中失效。热点数据存在、缓存失效后被大量请求同时重建,通常称为缓存击穿;大量不存在的数据反复回源才是这里的穿透。要根据数据库是否有数据、请求是否集中在同一键,以及回源量来判断,不能只凭“缓存命中率下降”下结论。
参考资料
- 国内:JavaGuide:Redis 常见面试题总结(下):用于说明穿透的触发条件,以及参数校验、缓存空值和布隆过滤器分别解决哪类请求。
- 国内:doocs:缓存雪崩、穿透和击穿:用于对比重复无效键和随机无效键,解释空值缓存的覆盖范围,以及穿透与热点失效的区别。
- 国内:JavaGuide:布隆过滤器详解:用于解释位数组、哈希碰撞、假阳性及不能直接清位删除元素的原因。
- 国外:Redis:Bloom filter:用于核对“未加入”与“可能加入”的判断语义,以及误判率、容量和扩展的成本。
- 国外:AWS:Caching challenges and strategies:用于说明负缓存与正常数据可使用不同 TTL,并结合下游错误语义和回源限制避免放大故障。



