java项目篇-布隆过滤器
文章目录
对于用户注册功能使用
直接查询数据库请求用户名是否存在。

存在什么问题?
● 海量用户如果说查询的用户名存在或不存在,全部请求数据库,会将数据库直接打满。
检查用户名是否存在引起的问题
1.用户名加载缓存
第一版解决方案,将数据库已有的用户名全部放到缓存里

该方案问题:
● 是否要设置数据的有效期?只能设置为无无有效期,也就是永久数据。
● 如果是永久不过期数据,占用 Redis 内存太高。
2.布隆过滤器
第二版解决方案,使用布隆过滤器。

2.1什么是布隆过滤器
布隆过滤器是一种数据结构,用于快速判断一个元素是否存在于一个集合中。具体来说,布隆过滤器包含一个位数组和一组哈希函数。位数组的初始值全部置为 0。在插入一个元素时,将该元素经过多个哈希函数映射到位数组上的多个位置,并将这些位置的值置为 1。
1字节(Byte)=8位(Bit)
总结就是:计算hash,不存储KV
add操作的时候hash%到的多个位置,全部置1,仅此而已!
应该是没有删除操作的,因为布隆过滤器返回的结果只有“可能存在”和“一定不存在”,万一你删除的位置是hash冲突的位置,其他的元素在这个位置也都全是1,你全部置0,那你就芭比Q了!

布隆过滤器存在判定原理:(被问到了,沟巴了,忘记了具体的原理了,面试又G了)
在查询一个元素是否存在时,会将该元素经过多个哈希函数映射到位数组上的多个位置,如果所有位置的值都为 1,则认为元素存在;如果存在任一位置的值为 0,则认为元素不存在。
出个题目检测学习情况:
Q:
布隆过滤器b为空,插入了俩元素e1,e2,有3个hash函数,如果插入到时候分别映射到索引为1,3,5和索引2,4,6,然后插入了e3,假如是映射到索引为1,2,3的位置,那么是不是发生冲突了?还是说映射到1,3,5或者是2,4,6才冲突?
A:
答案是映射到1,2,3的时候已经冲突了,1,3,5也冲突,2,4,6也冲突,只要映射到的索引位置对应元素全部是1,都是冲突的,映射到的索引位置对应元素有1个为0的话都不冲突!!!!
2.2优缺点
优点:
● 高效地判断一个元素是否属于一个大规模集合。
● 节省内存。
缺点:
● 可能存在一定的误判。
2.3 布隆过滤器误判理解
● 布隆过滤器要设置初始容量。容量设置越大,冲突几率越低。
● 布隆过滤器会设置预期的误判值。
误判能否接受
2.4 布隆过滤器的误判是否能够接受?
答:可以容忍。为什么?因为用户名不是特别重要的数据,如果说我设置用户名为 aaa,系统返回我不可用,那我大可以在 aaa 的基础上再加一个a,也就是 aaaa。
2.5 布隆过滤器流程图

3.代码中使用布隆过滤器
3.1引入 Redisson 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
</dependency>
3.2 配置 Redis 参数
spring:
data:
redis:
host: 127.0.0.1
port: 6379
password: 123456
3.3 创建布隆过滤器实例
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 布隆过滤器配置
*/
@Configuration
public class RBloomFilterConfiguration {
/**
* 防止用户注册查询数据库的布隆过滤器
*/
@Bean
public RBloomFilter<String> userRegisterCachePenetrationBloomFilter(RedissonClient redissonClient) {
RBloomFilter<String> cachePenetrationBloomFilter = redissonClient.getBloomFilter("xxx");
cachePenetrationBloomFilter.tryInit(0, 0);
return cachePenetrationBloomFilter;
}
}
tryInit 有两个核心参数:
● expectedInsertions:预估布隆过滤器存储的元素长度。
● falseProbability:运行的误判率。
错误率越低,位数组越长,布隆过滤器的内存占用越大。
错误率越低,散列 Hash 函数越多,计算耗时较长。
一个布隆过滤器占用大小的在线网站:https://krisives.github.io/bloom-calculator/
使用布隆过滤器的两种场景:
● 初始使用:注册用户时就向容器中新增数据,就不需要任务向容器存储数据了。
● 使用过程中引入:读取数据源将目标数据刷到布隆过滤器。
3.4 布隆过滤器判断是否存在元素。
private final RBloomFilter<String> userRegisterCachePenetrationBloomFilter;
@Override
public Boolean hasUsername(String username) {
return !userRegisterCachePenetrationBloomFilter.contains(username);
}
4. Q&A
4.1 用户注册功能
Q:如何防止用户名重复?
A:通过布隆过滤器把所有用户名进行加载。这样该功能就能完全隔离数据库。
数据库层面添加唯一索引。
4.2 如何防止恶意请求毫秒级触发大量请求去一个未注册的用户名?
因为用户名没注册,所以布隆过滤器不存在,代表着可以触发注册流程插入数据库。但是如果恶意请求短时间海量请求,这些请求都会落到数据库,造成数据库访问压力。这里通过分布式锁,锁定用户名进行串行执行,防止恶意请求利用未注册用户名将请求打到数据库。

4.3 如果恶意请求全部使用未注册用户名发起注册
结论:系统无法进行完全风控,只有通过类似于限流的功能进行保障系统安全
更多推荐
所有评论(0)