在系统设计阶段提前规划缓存穿透与缓存击穿的应对方案,是保障高并发下缓存稳定性的核心思路,选型时需结合业务场景、性能开销和运维成本综合决策。
缓存穿透和缓存击穿的区别是什么
缓存穿透和缓存击穿虽然名字相似,但产生原因和应对方式完全不同,理解它们的区别是选型的第一步。
- 缓存穿透:请求的数据在缓存和数据库中都不存在,导致每次请求都直接穿透缓存访问数据库,常见场景包括恶意攻击、参数错误,或者大量请求携带不存在的ID。
- 缓存击穿:某个热点key在缓存失效的瞬间,大量并发请求同时访问该key,直接压到数据库,常见于热门商品、活动数据等。
从影响来看,缓存穿透可能导致数据库负载持续升高,甚至拖垮整个系统;缓存击穿则会在短时间内造成数据库压力峰值,引发雪崩,业内专家指出,区分这两个概念是选型的基础,因为应对策略完全不同。
| 特性 | 缓存穿透 | 缓存击穿 |
|---|---|---|
| 数据存在性 | 数据库不存在 | 数据库存在但缓存失效 |
| 并发特点 | 多个不同key | 单个热点key |
| 解决方案核心 | 过滤不存在key | 控制并发 |
缓存穿透的产生原因
- 业务代码对无效参数未做校验,比如请求ID为负数或超长字符串。
- 恶意攻击者构造大量不存在的数据请求,试图绕过缓存。
- 缓存未设置空值过期策略,导致每次查询都落库。
缓存击穿的产生原因
- 热点key过期时间设置不合理,比如所有活动数据同时过期。
- 并发量高且没有降级机制,所有请求同时到达数据库。
- 缓存更新策略未考虑并发场景,导致缓存重建时出现竞态。
缓存穿透的应对思路选型对比
针对缓存穿透,业界有几种成熟方案,选型时需要对比它们的特点。
布隆过滤器
布隆过滤器使用位数组和哈希函数判断key是否可能存在,如果判断不存在,则直接返回,避免查库。

实操步骤:
- 在系统启动时,将数据库中所有合法key初始化到布隆过滤器。
- 请求到来时,先检查布隆过滤器,如果判断不存在,则直接返回错误或空。
- 如果判断可能存在,则继续查缓存或数据库。
选型考虑:
- 内存占用低,仅需少量位数组,百万级数据量下内存占用仅几MB,但有一定误判率,通常控制在1%以下。
- 查询速度快,O(k)复杂度,哈希函数个数一般设为3-5个。
- 适用场景:数据量较大、key集合相对固定的场景。
空值缓存
当缓存和数据库中都没有数据时,缓存一个空值并设置较短的过期时间,避免重复穿透。
选型考虑:
- 实现简单,但会占用额外缓存空间,过期时间一般设为5-10分钟。
- 需要限制空值缓存数量,防止恶意数据填充。
- 适合数据量不大、空值重复请求较多的场景。
参数校验
在代码层对请求参数进行合法性校验,过滤明显无效的请求,比如限制ID范围、长度、格式。
选型考虑:
- 成本低,但无法防御精心构造的合法参数穿透。
对比表格
| 方案 | 内存占用 | 性能影响 | 复杂度 | 适用场景 |
|---|---|---|---|---|
| 布隆过滤器 | 低 | 低 | 中 | 数据量大、key较固定 |
| 空值缓存 | 高 | 低 | 低 | 空值重复多 |
| 参数校验 | 无 | 无 | 低 | 基础过滤 |
行业共识认为,布隆过滤器在大规模穿透防御中效果最好,但需要维护数据同步,动态更新key集合时成本较高。
高并发场景缓存击穿怎么处理
缓存击穿的处理核心在于控制并发,避免数据库被热点key失效瞬间的请求打垮。
互斥锁
互斥锁允许缓存失效时只有一个线程去查询数据库,其他线程等待或返回默认值。
实操步骤:
- 使用Redis的
SETNX命令尝试获取锁,key为缓存key+lock后缀,超时时间设为50ms,重试次数3次。 - 获取成功则查询数据库,将结果写入缓存并设置过期时间,然后删除锁。
- 获取失败则等待一段时间后重试,或直接返回旧缓存(如果允许)。

选型考虑:
- 能有效防止击穿,但会降低并发吞吐量,增加响应延迟。
- 需要设置锁的超时时间,防止死锁,同时使用双重检查模式减少锁竞争。
- 适合对一致性要求不高的场景,允许短暂的不一致。
永不过期策略
为热点key设置永不过期,但通过异步任务定期更新缓存数据。
实操步骤:
- 缓存key不设置过期时间,存储一个额外的过期时间字段。
- 后台线程或定时任务定期检查,如果实际数据过期,则重新加载并更新缓存,可以使用消息队列或分布式定时任务驱动。
选型考虑:
- 避免并发击穿,但需要额外维护更新逻辑,可能造成数据不一致。
- 适合对实时性要求不高的场景,如配置信息、基础数据。
预热
在系统启动或活动开始前,将热点数据提前加载到缓存,并设置随机过期时间避免同时失效。
实操步骤:
- 活动开始前5分钟触发预热任务,使用分布式锁保证多节点不冲突。
- 将热点数据写入缓存,过期时间在基础时间上增加随机偏移。
选型考虑:
- 减少缓存失效瞬间的并发压力。
- 需要提前识别热点数据,适用于可预知的场景,如秒杀活动。
选型时要提前考虑的综合因素
选型不是简单选一个方案,而是需要结合业务、性能、运维多方面权衡。
业务场景分析
- 数据是否频繁变化?如果变化不频繁,布隆过滤器+空值缓存组合效果好。
- 并发量级如何?高并发场景下互斥锁可能成为瓶颈,可以考虑永不过期+异步更新。
- 数据一致性要求?强一致性要求的话,预热+互斥锁更可靠。
性能预算
- 每个方案对CPU、内存的影响,据统计,布隆过滤器内存占用仅为传统HashMap的1/10左右。
-

响应时间要求:互斥锁会增加等待时间,需要评估是否可接受,比如50ms的等待是否在业务容忍范围内。
运维成本
- 布隆过滤器需要维护数据同步,如果key集合动态变化,更新成本较高,需要定期全量重建。
- 空值缓存需要定期清理,防止堆积,可以通过LRU淘汰策略辅助。
- 互斥锁需要监控锁超时和死锁,做好日志记录。
组合使用
多数情况下,单一方案无法覆盖所有场景,常见的组合是:
- 基础防护:参数校验 + 空值缓存(针对常见穿透)
- 高并发防护:布隆过滤器(针对大量不存在key)+ 互斥锁(针对热点key击穿)
- 兜底策略:熔断降级、数据库限流,确保极端情况下系统可用。
场景示例:电商秒杀活动
秒杀活动存在大量热点商品,同时面临恶意用户请求不存在的商品ID,选型时可以这样组合:
- 使用布隆过滤器过滤不存在的商品ID,减少数据库无效查询。
- 对热点商品key采用互斥锁,防止缓存失效时击穿。
- 活动开始前预热商品数据,设置随机过期时间。
- 代码层增加参数校验,限制ID长度和格式。
缓存穿透与缓存击穿选型常见问题解答
Q: 缓存穿透和缓存击穿选型时应该优先考虑哪种方案?
A: 优先考虑业务场景,如果数据不存在请求多,优先布隆过滤器或空值缓存;如果热点key失效并发高,优先互斥锁或永不过期,没有通用方案,需结合数据特征和性能要求。
Q: 高并发场景下缓存击穿使用互斥锁还是布隆过滤器?
A: 布隆过滤器主要用于防御穿透,不能直接解决击穿,击穿的核心是并发请求同一失效key,互斥锁是直接方案,布隆过滤器可辅助减少部分击穿概率,但无法完全替代互斥锁。
Q: 选型时如何评估缓存穿透防御方案的成本?
A: 成本包括开发成本、硬件成本和运维成本,布隆过滤器引入额外编码和同步逻辑;空值缓存增加缓存空间;互斥锁降低并发性能,选型时需权衡,避免过度设计,选型时建议从业务需求出发,选择最简可行方案,避免过度设计。