上一篇文章中,我们介绍了常见的缓存架构。 常见缓存架构 — 穿透型缓存与旁路型缓存
缓存对于查询压力很大的服务来说是必不可少的解决方案,对于访问频率极高及读多写少的业务来说,使用缓存提升服务性能,减轻后端服务器压力等方面有着很好的效果。 但是,没有任何一种技术方案是只有好处没有弊端或风险的,本文我们就来详细介绍一下在缓存使用过程中可能带来的风险与解决办法。
在服务代码编写过程中,最应警惕的就是雪崩效应的发生,在缓存的使用过程中,缓存雪崩的问题也需要提前考虑和处理。 缓存雪崩指的多个缓存中的数据同时失效,此时新的数据请求全部 MISS,造成后端服务压力突增的现象,此时巨大的压力可能造成服务响应延迟甚至后端服务器崩溃。 除此之外,当缓存机器宕机,对于通常使用的旁路型缓存架构,所有请求都会直接请求道后端服务器,从而造成雪崩效应。
缓存击穿问题出现在缓存中存在某个极为热点的数据,一旦该数据过期,大量请求立即穿透到后端服务器,造成后端服务器压力的激增甚至宕机。 相比于缓存穿透,缓存击穿有时更加难以发现,表现为缓存运行情况一切正常,后端服务器请求量突然出现峰值,如果不加关注可能就会忽略掉缓存击穿问题的存在,从而埋藏下十分危险的安全隐患。
缓存穿透是缓存使用中十分常见的一个问题,也是恶意攻击的一个常见手段。 无论是穿透型缓存还是旁路型缓存,只要缓存中不存在被请求数据,都会到后端服务器尝试获取。 那么,只要通过构造一定不存在的数据,大量请求服务器,这些请求流量就会直接压到后端服务器,进而影响甚至压垮服务器。
布隆过滤器本质上是一种设计巧妙的概率型数据结构,通过高效的查询,能够快速告诉你某条数据一定不存在还是可能存在,因为他占用空间小、查询速度快等优势被广泛使用。
如果能够将后端数据库中所有数据都载入到缓存中,就不会发生缓存穿透问题了,因为此时一旦在缓存中没有查找到数据,就说明后端数据库中也并不存在该数据,就没有必要穿透到后端数据库再次访问了。 问题在于缓存的内存空间有限,无法将所有数据载入到缓存中,只能按照我们的策略缓存部分热点数据。 但是我们沿着这条思路继续思考,如果不缓存全部数据,而是改为缓存全部数据的 hash 值,就可以大幅缩小数据占用的缓存空间了,虽然这样我们没办法确认在缓存中已存在 hash 值的数据在后端数据库是否真实存在,因为不同的数据 hash 值可能相同,但对于不存在 hash 值的数据,我们就不再需要访问后端数据库了,从而很大程度上可以避免缓存穿透问题。
我们可以在缓存中通过一个 bit 数组来实现,这与 bitmap 算法如出一辙。
此时我们通过三种 hash 算法分别计算数据的 hash 值,例如我们的数据是 "baidu",计算出来的 hash 值分别是 1、4、7,于是我们将对应的位置位。
接下来,我们再插入一条新的数据 “tencent”,hash 值分别是 3、4、8,于是我们将对应的位置位:
那么,这时请求数据 "alibaba",假设这条数据通过三种 hash 算法计算出的值为 1、2、8,我们看到,在 bit 数组中,2 没有被置位,于是我们可以立即判断出 alibaba 并不在数据库中。 而如果请求的数据通过三种 hash 算法计算出的值为 1、3、8,由于这三个位置都已经被置位,那么这个数据可能存在也可能不存在。
下面是一个布隆过滤器误报率的统计图:
图中,k 为哈希函数个数,m 为布隆过滤器长度,n 为插入的元素个数,p 为误报率。 可以推导出下面的公式:
布隆过滤器使用相对很小的内存开销,通过 bitmap 算法实现了一个概率模型,以一定概率对不存在数据的请求可以在第一时间返回不存在,从而避免了缓存穿透的风险。 其优点是显而易见的:
但其缺点也同样明显:
https://hackernoon.com/probabilistic-data-structures-bloom-filter-5374112a7832。 https://www.jasondavies.com/bloomfilter/ https://www.jianshu.com/p/2104d11ee0a2。 https://baijiahao.baidu.com/s?id=1619572269435584821&wfr=spider&for=pc。