缓存穿透、缓存击穿、缓存雪崩场景以及解决方案 - 详解
2026-01-19 16:12 tlnshuju 阅读(0) 评论(0) 收藏 举报缓存穿透 (Cache Penetration)
场景描述
缓存穿透是指查询一个不存在的数据,导致每次请求都绕过缓存直接查询数据库(或后端服务)。这通常发生在恶意攻击或无效查询频繁发生时,如果缓存中没有对应 key(或缓存 null 值未处理),会造成数据库压力过大,甚至崩溃。核心问题是:缓存无法“拦截”无效请求,导致后端被洪峰淹没。
例子
- 电商场景:黑客或爬虫使用随机生成的商品 ID(如 "product:999999",实际不存在)反复查询库存。缓存 miss 后直击数据库,数据库要求反复执行 "SELECT * FROM products WHERE id=999999",返回空结果。如果并发高(每秒 1000 次),数据库 CPU/IO 飙升,导致正常查询变慢。
- 用户系统:查询不存在的用户 ID(如伪造的用户名),例如在登录架构中反复尝试不存在的账号,绕过缓存直接查 DB。
解决方案
- 缓存空值:在查询到 null 时,将空值(一个占位对象如 "NULL")存入缓存,并设置较短的过期时间(TTL,如 5-10 分钟),防止反复穿透。缺点:占用缓存空间,如果空 key 太多可能导致内存膨胀。
- 布隆过滤器 (Bloom Filter):在缓存前加一层布隆过滤器(一个位图结构),预先将所有存在的 key 哈希存入。查询时先查过滤器,如果不存在直接返回空,不查缓存/DB。适用于 key 空间大的场景(如亿级 ID)。实现:用 Redis 的 Bitmap 或 Guava 的 BloomFilter。
- 接口限流/验证:在 API 层加参数校验(如 ID 格式验证)或限流(e.g., 采用 Sentinel 或 RateLimiter),防止恶意请求。结合 CAPTCHA 或 IP 黑名单。
- 异步回填:如果业务允许,收集无效查询日志,分析攻击模式,动态调整过滤规则。
缓存击穿 (Cache Breakdown)
场景描述
指一个就是缓存击穿热点 key(高频访问的数据)在失效瞬间,大量并发请求同时 miss 缓存,直击数据库,导致数据库瞬时压力激增。不同于穿透,这里的 key 是存在的,但过期了。核心障碍是:单个热点 key 的失效引发“狗堆”效应(thundering herd),多个线程同时加载材料。
例子
- 社交媒体:一个热门帖子的点赞数(key: "post:123:likes")缓存过期时间为 1 小时。正好在过期时刻,有 1000 个用户同时刷新页面,全部 miss 缓存,同时查询 DB "SELECT COUNT(*) FROM likes WHERE post_id=123",数据库瞬间负载从 10 QPS 跳到 1000 QPS,导致响应延迟从 10ms 升到 1s。
- 秒杀活动:库存 key(如 "seckill:product:456:stock")在活动高峰期失效,大量用户抢购请求同时打到 DB,造成数据库锁争用或连接池耗尽。
解决方案
- 互斥锁加载:运用分布式锁(如 Redis 的 setnx 或 Redlock)确保只有一个线程加载数据,其他线程等待或返回旧值。Caffeine 的 LoadingCache 支持同步加载(仅一个线程 load,其他阻塞)。
- 永不过期 + 后台刷新:热点 key 设置为永不过期,使用 Caffeine 的 refreshAfterWrite 或定时任务异步刷新材料。缺点:数据可能短暂不一致。
- 随机化过期时间:为热点 key 添加随机偏移(如 baseTTL + random(0~300s)),避免同时失效。
- 多级缓存:用 Caffeine (L1) + Redis (L2),L1 miss 时从 L2 加载,降低 DB 压力。结合预热:提前加载热点 key。
缓存雪崩 (Cache Avalanche)
场景描述
指就是缓存雪崩大量 key 同时失效(或缓存服务整体宕机),导致海量请求同时绕过缓存直击数据库,造成数据库崩溃或平台瘫痪。核心问题是:批量失效引发洪峰,通常由于 key 过期时间相同、缓存集群故障或突发流量引起。
例子
- 新闻网站:热门新闻的缓存 key 都设置了相同的 30 分钟 TTL(如 "news:headline:1" 到 "news:headline:100")。在 30 分钟到期时,正值访问高峰,所有 key 同时失效,数万请求瞬间打到 DB 执行繁琐查询(如 JOIN 多表),数据库连接池满,导致 503 错误,用户看到“服务不可用”。
- 电商促销:双 11 期间,所有促销商品的库存/价格 key 因相同过期策略(如整点失效)同时过期,加上突发流量,Redis 集群压力大但 DB 直接崩( MySQL 从 100 QPS 飙到 10 QPS)。
解决方案
- 打散过期时间:为 key 设置随机 TTL(如 baseTime + random(0~600s)),避免集中失效。Redis 支持 lua 脚本批量设置。
- 自动刷新机制:用 Caffeine 的 refreshAfterWrite 或 Redis 的惰性删除 + 主动刷新,热点 key 在访问时异步更新。
- 降级/熔断:集成 Hystrix 或 Sentinel,在雪崩时熔断请求,返回默认值或错误页,保护 DB。结合限流,限制每秒请求数。
- 高可用架构:Redis 用主从 + Sentinel/Cluster 集群,确保单点故障不影响整体。预热缓存:服务启动或定时从 DB 加载热点内容。监控工具(如 Prometheus)预警高 miss 率时报警。
浙公网安备 33010602011771号