缓存击穿是单个热点key过期引发的瞬时并发洪峰,本质为“点状失效+高并发访问”,仅影响一个key但qps可达数万;典型表现为redis命中率局部骤降、某sql执行频率突增10倍以上、接口延迟集中持续1–5秒;常用解决方案为互斥锁或永不过期+异步刷新。

缓存击穿是单个热点key过期引发的瞬时并发洪峰
击穿的本质是“点状失效+高并发访问”,只影响一个key,但该key的QPS可能高达数万。比如商品详情页的product:10086在过期瞬间被10000个用户同时请求,全部穿透到数据库查同一条记录。
关键判断信号:Redis缓存命中率局部骤降、数据库某条SQL执行频率突增10倍以上、响应延迟集中在某个接口且持续1–5秒。
- 不能靠加数据库索引或扩容解决——问题不在查询慢,而在同一毫秒内涌进太多相同请求
- 加互斥锁(如
SET key value EX 10 NX)是最常用手段,但要注意锁超时必须短于业务查询耗时,否则会卡住后续请求 - 永不过期+异步刷新适合更新不频繁的热点数据,但要警惕脏数据风险:如果后台任务失败,缓存就长期不更新
缓存雪崩是批量key集中失效或服务不可用导致的系统性崩溃
雪崩的本质是“面状失效”,影响范围不是某个key,而是成百上千个key同时过期,或者整个Redis集群宕机。典型如大促前统一设了EXPIRE 86400,零点一到,所有商品缓存集体消失。
和击穿最明显的区别:数据库压力不是集中在某几条SQL,而是连接池耗尽、慢查询告警批量爆发、多个业务接口同时超时。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 随机过期时间必须落实到代码层,不能只靠运维配置——比如基础TTL设为3600秒,再叠加
random.randint(0, 600)秒偏移 - 本地缓存(如Caffeine)只能缓解雪崩初期压力,无法替代Redis;它对击穿无效,因为本地缓存同样会过期
- Redis高可用必须做主从+哨兵或Cluster,但光有集群不够——要验证故障转移时间是否
穿透与击穿/雪崩的根本分界线在“数据是否存在”
很多人混淆穿透和击穿,其实只要看数据库返回结果:击穿时db.query()返回非空,穿透时db.query()永远返回null。前者是“有数据但缓存断了”,后者是“压根没这数据”。
所以防御策略完全相反:击穿要防并发查同一条数据,穿透要防无效key反复打库。
- 空值缓存(
redis.setex("user:-1", 300, "null"))必须配合参数校验,否则恶意构造user:9999999999999会导致缓存里堆满垃圾key - Bloom Filter适合写多读少场景,但要注意误判率——生产环境建议控制在
0.01%以内,否则正常用户也会被误拦 - 穿透攻击往往伴随大量
404响应和slowlog中empty result占比突增,这是比QPS更早的预警指标
兜底方案必须按失效粒度分层部署
没有银弹方案。击穿靠锁/永不过期,雪崩靠随机TTL+集群容灾,穿透靠校验+Bloom Filter,三者必须同时存在,但启用条件要严格隔离。
- 互斥锁不能用于穿透场景:对
user:abc这种非法格式key加锁,等于主动帮攻击者耗尽Redis连接 - 本地缓存不能代替空值缓存:Caffeine对
null默认不缓存,需显式调用cache.put(key, Optional.empty()) - 所有兜底逻辑必须带熔断开关,比如当
redis.ping()失败超过3次,自动降级为空值缓存+参数校验,避免雪崩时还拼命连Redis
最容易被忽略的是监控联动——击穿需要监听redis.keyspace@0:expired事件+DB慢日志聚合,雪崩则必须监控redis.info memory | grep "used_memory_peak_perc"和连接池活跃数。没这些指标,再好的方案也形同虚设。










