Redis缓存穿透防护策略解析,分享实战案例与解决方案
最近,有开发团队反映,在晚上促销时,系统因为大量请求不存在的商品数据,导致数据库压力骤增,服务响应变慢。这种情况再次提醒我们,缓存穿透问题不容忽视。
什么是缓存穿透
简单来说,缓存穿透就是有人一直请求缓存里没有的数据。这些请求每次都会绕开缓存,直接打到数据库上。如果这种请求很多,数据库就可能被压垮。这不像缓存雪崩(大量缓存同时失效)或缓存击穿(一个热点缓存失效),因为穿透是请求的数据根本不存在。
常见的原因有两个。一是业务代码有bug,或者参数传错了。二是恶意攻击,有人故意用大量不存在的关键词来刷你的接口。
核心防护策略
最直接的办法,就是如果数据库里查不到,也在缓存里存一个空值。比如,对于查询商品ID=99999的请求,数据库里没有,那就在Redis里存一个 key 为 `product:99999`,value 为一个特殊的空标记(比如字符串`"NULL"`),并设置一个较短的过期时间,比如30秒。这样,短时间内再有同样的请求,就直接从缓存拿到空值返回,不会去查数据库了。但要注意,如果攻击者用大量不同的、随机生成的ID来请求,这个方法效果会打折扣,因为缓存会被大量无用的空值占满。
另一个更常用的方法是布隆过滤器。你可以把它想象成一个很大的、很节约空间的“名单”。在系统启动时,可以把所有可能被查询的数据的ID(比如所有有效的商品ID)预先放到这个过滤器里。当一个查询请求过来时,先让布隆过滤器检查一下:这个ID在不在“名单”里?如果布隆过滤器说“不在”,那这个ID肯定不存在,请求就可以直接返回空,不用去查缓存和数据库了。如果布隆过滤器说“在”,那这个ID“可能”存在(有极小的误判率),这时再去按正常流程查询缓存和数据库。这相当于在缓存前面加了一道高效的检查哨。
实战案例分享
一个做内容推荐的团队遇到过这样的问题。他们的应用有一个根据用户ID查询其偏好的接口。有一次,因为上游数据同步出错,导致一小部分用户ID失效了,但前端的请求还在不断发过来。这些请求的ID在数据库里没有对应的记录,瞬间造成了缓存穿透。
他们的解决方案是双管齐下。首先,对这类查询接口,如果数据库返回“查无此人”,他们立刻在Redis里用这个用户ID作为key,设置一个值为`null`的缓存项,过期时间设为5分钟。这解决了短时间内同一ID的重复攻击。其次,他们在一个独立的服务里启动了一个布隆过滤器,并定期(比如每天凌晨)从数据库把所有有效的用户ID同步到过滤器里。所有查询请求在处理前,都必须先经过这个过滤器的检查。这样一来,对于根本无效的ID,在入口处就被拦截了,大大减轻了后端压力。
实施后效果很明显。在遇到异常数据或恶意爬虫试探时,数据库的负载曲线变得非常平稳,没有再出现因为缓存穿透导致的CPU飙升情况。
总结与建议
处理缓存穿透,没有“一招鲜”的万能方法。在实际项目中,建议根据业务场景组合使用。对于已知的、有限的、变化不频繁的数据集(比如国家地区编码、商品品类),使用布隆过滤器效果最好。对于变化频繁的数据,或者无法预知所有合法键的情况,缓存空对象是一个更灵活的选择,但要小心设置合理的过期时间,并监控缓存内存的使用情况。
最重要的是,要在系统设计之初就考虑到这种异常流量场景,并把防护措施作为缓存方案的标准组成部分,而不是出了问题再补救。
引用来源
本解析基于Redis官方文档关于缓存模式的论述,并结合了多个技术社区(如Stack Overflow、国内CSDN博客、开发者论坛)中关于实际应对缓存穿透的讨论案例进行综合阐述。其中,布隆过滤器的实现原理参考了经典算法教材,而实战案例则整理自某互联网公司技术团队的内部项目复盘报告。