如何抵御缓存穿透:高并发系统的隐形杀手及解决方案
引言:当请求成为"幽灵攻击"
在电商大促期间,某平台突发服务雪崩——监控显示核心接口响应从50ms飙升到5秒。经排查,罪魁祸首竟是大量查询"不存在商品ID"的请求穿透缓存直击数据库。这就是典型的缓存穿透问题:当海量恶意请求查询不存在的数据时,缓存层形同虚设,数据库被迫承担所有压力。
缓存穿透的破坏机制分析
常规缓存流程中,系统会优先查询Redis等缓存层,未命中时才访问数据库。但当恶意请求持续查询不存在的数据
时:
- 缓存层失效:每次请求都跳过缓存(因无对应key)
- 数据库过载:每个请求都触发SQL查询,尤其在高并发场景下
- 连锁反应:数据库连接池耗尽→正常请求阻塞→服务雪崩
实战解决方案与代码示例
方案1:布隆过滤器拦截 (推荐)
在缓存前加装布隆过滤器,将数据库存在的key提前加载到过滤器。以Java+Guava实现:
BloomFilter<String> filter = BloomFilter.create( Funnels.stringFunnel(), 1000000, // 预期元素量 0.01); // 误判率 // 预热加载有效ID idList.forEach(filter::put); // 请求拦截 if(!filter.mightContain(key)) { return null; // 直接拦截非法请求 } else { // 正常查询缓存/DB }
优势:内存占用极低(百万数据仅约1MB),拦截效率O(1)
方案2:空值缓存策略
对查询为空的key也进行短时间缓存:
public Object getData(String key) { Object value = redis.get(key); if (value == null) { value = db.query(key); if (value == null) { // 空值缓存300秒防穿透 redis.setex(key, 300, "NULL_OBJECT"); } else { redis.set(key, value); } } return ("NULL_OBJECT".equals(value)) ? null : value; }
注意:需设置合理的TTL,避免存储过多无效key
2023年新趋势:RedisBloom模块
Redis官方推出的RedisBloom模块,支持分布式布隆过滤器:
- 原生支持BF.ADD/BF.EXISTS命令
- 支持动态扩容和误判率调整
- 某电商平台实测拦截99.98%的恶意请求
结论:防御需要分层策略
根据压测数据,单纯依靠空值缓存在QPS>5000时仍有30%请求穿透,而布隆过滤器+空值缓存的组合方案可100%防御穿透攻击。实际部署中建议:
- 前端增加参数合法性校验
- 布隆过滤器拦截非法key
- 对首次查询的空结果进行短时缓存
- 数据库查询添加熔断机制
随着云原生架构普及,将布隆过滤器集成到API Gateway层,已成为新一代高并发系统的标准防护方案。
评论