vlambda博客
学习文章列表

布隆过滤器 有我在 请别害怕~

 Redis缓存被击穿,用我就够啦。


相信大家一定知道过Redis的大名吧,吞吐率高达10Wqps+的它可谓是在高并发场景下大放异彩,不过它也会存在致命的缺陷,今天我们就来说说,它在高并发情况下的几大问题。


Redis 



Redis的作用有很多,常用来实现分布式锁(保证数据一致性)、缓存(减少数据库压力)、接口防刷限流、过期订阅等。


    在我们后端系统的开发中,根据域名DNS轮询、Nginx负载均衡、微服务弹性部署。我们所有的服务都能够轻松的进行弹性扩容伸缩,但是为了保证数据一致性,我们的数据库往往不能够像增加服务那样,批量部署。这就导致了数据库成为了系统的瓶颈,为了解决此类问题,除了分库分表合理设计以外,更加常用的就是使用中间件缓存,减少我们数据库的压力,这里要说的就是我们的Redis。

布隆过滤器 有我在 请别害怕~

01


Reids 缓存击穿问题


      由于Redis工作在内存数据的特殊性,它的性能比我们常用数据库强悍太多,所以我们常用它来进行存放热点数据,当用户的请求来临时,会首先进入到我们的Redis进行查询,如果Redis中存在数据,则直接进行返回,不需要在对数据库进行查询。由此可见,在后端业务设计中,缓存的命中率是很重要的指标,缓存命中率的提高能够大大释放数据库的压力。

    那么设想,假设有一条根本不存在的记录,通过某个请求进行查询,在我们的Redis中没有查询到,根据业务我们到Mysql进行查询,发现还是不存在,返回Null,如果我们不对此条记录进行处理,那么下一次该记录再次请求时,我们还需要访问Mysql,到了这个时候,我们的缓存等于已经失去了作用,无法抵御一条不存在的记录,假设这个请求量是庞大的,那么极有可能造成我们的数据库主机CPU负载飙升,影响其他业务的运作,还可能导致Mysql主机宕机,造成整个后端系统的崩溃,那么这个就是我们所说的缓存击穿问题。

布隆过滤器 有我在 请别害怕~

02


如何解决缓存击穿问题



缓存击穿就是我们的Redis缓存无法拦截请求,使无效记录的请求请求到了Mysql,造成Mysql的崩溃。

          那么假设我们将每次查询的记录存入到Redis中,存放值为NULL,那么下次该记录再次查询时,我们就不需要经过Mysql了,这样不就很好的避免了缓存击穿的问题吗?

       这个想法貌似很好的解决了缓存击穿的问题,但是在服务器上,内存资源非常宝贵,假设无效记录的请求足够大,对所有无效记录一条一条都进行存储的话,那么我们的内存可能会面临溢出的危险,这对整个系统是不利的。


     那么,如何才能有一个既不浪费资源,又能够解决缓存击穿的方案呢?


我们的主角 ——布隆过滤器来啦。



03


布隆过滤器


布隆过滤器本质上是一种数据结构,特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”。

相比于传统的 List、Set、Map 等数据结构,它更高效、占用空间更少,但是缺点是其返回的结果是概率性的,而不是确切的。


布隆过滤器数据结构 

size  bit数组长度

byte[]  数组 只存放0和1 1代表存在

散列函数个数

布隆过滤器的 数组长度和 散列函数个数关系到布隆过滤器的精确度

根据用户输入的数组长度和 精确度 布隆过滤器能通过算法计算出所需散列函数个数。

 @VisibleForTesting
static long optimalNumOfBits(long n, double p) {
if (p == 0) {
p = Double.MIN_VALUE;
}
return (long) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));
}

存放原理

  布隆过滤器只拥有初始化 方法 和 put(添加)及 get(判断是否存在的方法) 

当往布隆过滤器添加一条数据的时候,数据的实际值并不会进行存储。


假定我们初始化的大小是100    散列值是3个 


那么我们每次插入数据时 根据键会计算出三个不同散列值,通过散列值对数组长度进行求余,将计算的三个值在数据指定的地方变更为1 。


判断是否存在原理

    检索键值是否存在时,通过散列方法对数组长度求余,判断3个点是否等于1如果等于1则可能存在(因为求余特性,其他键也可能会生成在此的散列值,这就是布隆过滤器存在精准问题的地方),如果不等于1 则必定不存在。


图片来源百度图片 如有侵权 请联系作着删除


因为布隆过滤器并不实际存值,并且数组的求余特性,只需要我们设置数组长度和精确度,他能就只能计算出所需的散列函数,,一个 boolean 值占空间为 1byte一个预期以百万分之一的误判率对 200 万元素进行过滤或者说校验的布隆过滤器约占 7 mb 空间。


所以我们在系统开启初始化时,查询热点数据批量缓存到布隆过滤器中,当不存在的记录来临时,我们可以调用布隆过滤器的方法,判断是否存在如果不存在直接返回,这洋就解决了我们的缓存击穿问题啦。


写在最后

本文介绍了缓存穿透的场景和如何使用高性能的布隆过滤器处理缓存击穿的方案,介绍了布隆过滤器的工作机制,由于笔者知识水平有限,如有不对之处欢迎指正。