解决缓存穿透
缓存穿透是因为数据本身不存在而引起的,所以我们就要想办法在确认数据不存在之后,避免下一次查询再次落到数据库上。这里有两种解决思路。
回写特殊值
第一种思路是在缓存未命中,而且数据库也没有的情况下,往缓存里写入一个特殊的值。这个值就是标记数据不存在。那么下一次查询请求过来的时候,看到这个特殊值,就知道没有必要再去数据库查询了。
你可以直接介绍这个方案
第一种思路是回写特殊值,也就是在第一次查询发现数据里都没有数据的时候,直接写入一个特殊值。那么下一次查询过来的时候,看到缓存里的特殊值,就知道没有数据,这时候直接返回就可以了。在这种设计下,数据库只需要支撑住第一次请求就可以。
但是这个方案也是有缺点的。
如果攻击者每次都用不同且不存在的key来请求数据,那么这种措施毫无效果。并且,因为要回写特殊值,那么这些不存在的key都会有特殊值,浪费了Redis的内存。这可能会进一步引起另外一个问题,就是Redis在内存不足,执行淘汰的时候,把其他有用的数据淘汰掉。
这个时候可以引出下一个点了,考虑使用布隆过滤器。
布隆过滤器
既然缓存穿透是因为数据不存在,那么提前用布隆过滤器判断一下不就可以了吗。
正常请求一个key的流程如下:
如果请求一个 key 不存在,那么布隆过滤器会直接说数据不存在,那么就没必要继续往下查询了。
首先简单介绍这个流程
使用布隆过滤器的流程是业务代码收到请求之后,要先问一下布隆过滤器有没有这个key。如果说没有,就不用继续往后执行了。如果布隆过滤器说有,那么就继续往后执行,去查询缓存和数据库,并且查询到了数据的时候,回写到缓存里面。
然后你要记得介绍假阳性的问题。
但是布隆过滤器本身存在假阳性的问题,所以当攻击者请求一个不存在的key的时候,布隆过滤器可能会返回数据存在的假阳性响应。在这种情况下,业务代码依旧会去查询缓存和数据库。不过因为假阳性的概率很低,如果说概率是万分之一,就算攻击的并发有百万,也只有100个查询请求会落到数据库上。
这个时候可以补充一个变种
也可以考虑先查询缓存,当缓存中没有数据的时候,再去查询布隆过滤器。如果布隆过滤器说有数据,再去查询数据库。
然后你对比两者。
这两种模式没有太大的差别。先查询布隆过滤器,保护效果会更好,也就是提前挡住了非法请求。而先查询缓存,对正常请求更加友好,因为正常请求大概率命中缓存,直接返回数据,也就不用查询布隆过滤器了。
不过如果布隆过滤器也是在 Redis 的基础上实现的,两者就基本上没什么区别了。