上一篇文章聊了缓存更新的几中最佳实践策略,除此之外,在实际使用缓存的过程中,还需要考虑其他的几个问题,比如缓存穿透,缓存击穿,缓存雪崩,今天先学习并总结一下缓存穿透的概念以及解决方案。
缓存穿透
缓存穿透是指查询一个一定不存在的key,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击应用,该漏洞就存在高危状态。
常用解决方案
解决方案常见有三种,bitmap,bloom filter,缓存空对象。
bitmap
将所有的可能存在的key哈希后保存在一个足够大的bitmap中。所有的查询都优先经过该bitmap的过滤,经过bitmap过滤后,可以去除那些不存在的key,避免不存在的key的查询流到DB层,降低了无畏的查询和DB压力。
布隆过滤器(Bloom Filter)
bitmap在面临数据量足够大的时候,显现出来的疲惫性,是无法满足当前互联网发展的态势,所以就引入了一个著名的工业实现——布隆过滤器。布隆过滤器是引入了k(k>1)个相互独立的哈希函数,保证在给定的空间、误判率下,完成元素判重的过程。
布隆过滤器的原理也不复杂,主要原理:位数组+k个hash函数。
比如对字符串进行hash计算,将计算成的hash值作为下标,在位数组上将0换成1,循环K次,同一个字符串在位数组的位置可能出现K次。

基于位数组的布隆过滤器的缺陷
- 不能保证所有的查询的结果100%正确(容忍极低概率的错误)
- 不支持删除一个已经插入的关键字,因为该关键字对应的位会牵动到其他的关键字(可以改用count 数组)
缓存空对象
由于key永远不会存在,所以可以为该key缓存一个null值,并未它加上一个过期时间
基于缓存空对象的手段有两种显而易见的缺陷
- 系统可能缓存了大量了空值对象,意味着需要占用不少空间。比较有效且简单的解决办法是为该key设置一个过期时间,到期自动剔除。
- 在过期时间内,可能存在DB与缓存的数据不一致。(前文提到Cache Aside更新策略下的问题)
总结
1、缓存穿透问题是一个显而易见,并且极可能被其他人当做攻击对象的漏洞,需要慎重对待。
2、布隆过滤器用于处理大数据的查重效果极佳。在判断去重操作中,又get到一个新技能,不过要考虑到它的误判率。另外,由于数学能力有限,实在看不懂误判率的计算公式,凉了~
3、关于缓存穿透的几种解决方案,如何去抉择,应该是根据业务的实际情况去考虑,假如bitmap足以应付,为何还需要去用bloom filter呢?倘若空间在可控范围内,缓存空对象亦不是一无是处,简单粗暴未尝不是一个最佳实践。
4、从三种解决方案可以看出实际上为两种,一种是查重检查(bitmap,bloom filter),如同漏斗过滤,一种是查(缓存空对象),没有就生成一个给查。那么,是否还有第三种超脱以上两种的解决方案呢?
参考资料
https://www.jianshu.com/p/b0c0edf7686e
https://blog.csdn.net/fei33423/article/details/79027790
https://blog.csdn.net/zdxiq000/article/details/57626464