布隆过滤器解决 Redis 缓存穿透【绝对易懂】

1. 缓存穿透

在说布隆过滤器之前,我们先了解一下什么是缓存穿透?
在高并发环境下,某一个 key 被不停地访问,然而缓存中找不到这个 key,于是我们的程序就会到数据库中去读取,诶?数据库中也没有呀!好,它根本就不存在,全剧终。这会导致大量的无效请求都被打到数据库上, 而缓存中的这条数据依然为空。无效请求比如 id 为 -1 or 特别大不存在的数据,这时的用户很可能是攻击者,攻击会导致数据库压力过大,严重的时候会直接影响线上业务。

解决方案:
① 接口层添加校验,比如 id 小于 0 的请求直接拦截;
② 对查询接口为空的对象也进行缓存,将 null 也放入缓存中,设置好失效时间;
③ 使用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一定不存在的数据会被这个bitmap 拦截掉,从而避免了对底层存储系统的查询压力。

2. 布隆过滤器工作原理

在这里插入图片描述

注意,布隆过滤器只是用来判断某个数据是否存在于数据库中,它并不是用来查数据的,需要配合 Redis 使用!

(1)首先我们可以把它看成一个二进制数组,通过计算哈希值来检索数据的存储位置,初始情况下里面存储的数据全是 0,代表没有任何数据;

(2)布隆过滤器本身不存储数据,而是通过一定的哈希函数对数据库中的数据进行计算, 将其映射到一个固定长度的比特数组中,映射到数组的数据由 0 变成 1;

(3)查询的时候同样道理,先计算哈希值,然后到对应的位置找数据,如果为 1 代表有数据,为 0 则数据不存在;

(4)数据加载到布隆过滤器需要使用哈希函数计算存储位置,我们都知道哈希值是会重复的,这不就是说两个哈希值相同的数据,一个存在一个不存在,不存在的数据也能蒙混过关吗?这就是布隆过滤器的一个缺点 —— 误判;

(5)误判怎么办呢?我们在存入数据时仅使用了一次哈希函数计算存储位置,这哈希碰撞的概率也太大了,于是,布隆过滤器使用了多个不同的哈希函数,计算多个哈希值来解决这个问题,也就是说一个数据不只占有一个位置了,如果布隆过滤器内部用了 k 个哈希函数,那么它就会占用 k 个数组位置,同时这 k 个位置的数据都被设为 1。在查找的时候,我们也不再是只查一个位置,要确保这 k 个位置的值全为 1 才算存在,只要有一个为 0,就是不存在

扫描二维码关注公众号,回复: 16844480 查看本文章

(6)其实在真正使用的时候,我们不是直接设置哈希函数个数的,它给我们提供了一个接口方法,我们需要指定过滤器长度和误判率,过滤器会根据误判率来设置哈希函数的个数,当然误判率设置的越低,哈希函数的个数就要更多

(7)或许你也感觉到了,哈希函数越多,最终查询的正确性就会越高,那是不是我把误判率设的越小越好呢?既然越小越好,那直接设成 0 算了?nonono,有没有想过,这个时候你的哈希函数需要多少?数组空间需要多少?计算时间又需要多少?没错,追求正确性的代价就是放弃性能;

(8)以上所说的都是布隆过滤器的增加和查询数据,布隆过滤器是不支持删除操作的。 虽然说我们用了多个哈希函数来计算存储位置,尽可能保证每个数据的哈希位组合都是唯一的,但是对于每个哈希位来说,它存储的元素一般不止一个,这个标志位并不是你自己独有的,你删元素的时候把这个标志位设成了 0,那别的数据怎么办呢?你是不是把别人的也删了?所以对于布隆过滤器来说,删除操作是不可行的。

3. 配合 Redis 查询流程

最后说一下整体的查询流程:
首先数据库中的数据在布隆过滤器初始化的时候就被注册进去了,之后前端发起的每次查询请求,都会先经过布隆过滤器的筛选,如果标识位都为 1 说明数据存在,就去 Redis 里查数据,Redis 有则返回,没有就去数据库中查,将查询结果返回给前端并写入缓存中;
若标识位有一个不为 1,那就说明这个数据不存在,如果我们不作任何处理,大量的请求就会被打到数据库上,缓存穿透导致数据库压力过大,严重影响正常业务。所以对于这种不存在的数据,布隆过滤器可以直接把它拦截住,不去数据库中查,而是将错误信息返回给前端。

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_52861684/article/details/133299826