面试题
| 如何快速准备判断某一数据在海量数据中存在 |
| 了解布隆过滤器吗 |
| 安全网址判断,黑名单校验,识别垃圾邮件 |
| 白名单校验,识别合法用户? |
理论
由一个初始值都为0的 bit数组和多个哈希函数构成,用来快速判断集合中是否存在某个元素

设计思想
| 目的 | 减少内存占用 |
| 方式 | 不保存数据信息,只是在内存中做一个是否存在的标记flag |
判断具体数据是否存在于一个大集合中
特点
高效的插入和查询,占用空间少,返回的结果是不确定性和不够完美。
一个元素如果判断结果存在时,元素不一定存在,但是判断为不存在时一定不存在。(总结:有是可能有;无是肯定无)
布隆过滤器可以添加元素,但是不能删除元素,涉及到hashcode判断依据,删除元素会到处误判率增加。
原理
布隆过滤器(Bloom Filter)是一种专门用来解决去重问题的高级数据结构。
实际就是一个大型位数组和几个不同的无偏hash函数(无偏表示分布均匀)。由一个初始值为0的bit数组和多个哈希函数构成,用来快速判断某个数据是否存在。但是跟HyperLogLog一样,也存在不精确,存在误差。
添加KEY
使用多个 Hash 函数对key进行hash算法得到一个整数索引值,对位数组长度进行取模运算得到一个位置,每个Hash 函数都会得到一个不同的位置,将几个位置都置则完成添加操作。
查询KEY
只要有一位是 0,则表示 key 不能存在;如果都是 1,则不一定存在对应的key。
数据不精确
Hash 冲突导致数据不精确

查询某个变量只要看这些点是不是都是 1,就可以大概率知道集合中有没有它
如果这些点有任何一个为零则被查询变量一定不在,如果都是 1,则被查询变量很可能存
哈希函数
将任意大小的输入数据转换成特定大小的输出数据的函数,转换后的数据称为哈希值或哈希编码,也叫散列值
散列函数的输入和输出不是唯一对应关系,如果两个散列值相同,两个输入值可能相同可能不同(如下图中的56E5RD),这种情况叫做‘散列碰撞(collision)’



优势
- 正是基于布隆过滤器的快速检测特性,在把数据写入数据库时,使用布隆过滤器做个标记。
- 当缓存缺失后,查询数据库时,通过查询布隆过滤器快速判断数据是否存在。
- 如果不存在,就不再去数据库中查询,即使发生缓存穿透,大量请求只会查询Redis和布降过滤器,而不会积压到数据库,就不会影响数据库的正常运行。
- 布隆过滤器可以使用Redis实现,本身就能承担较大的并发访问压力。
使用步骤
1. 初始化 bitmap:由长度为m的位向量或位列表(仅包含 0 和 1 位值得列表)组成,初始值均为 0

2. 添加占坑位:对 KEY 进行多次hash(KEY ) ——> 取模运行 一> 得到坑位
3. 判断是否存在 :查询某个key是否存在时,先把这个 key 通过相同的多个 hash 函数进行运算,查看对应的位置是否都为 1,只要有一个位为零,那么说明布隆过滤器中这个 key 不存在;如果这几个位置全都是 1,那么说明极有可能存在;

总结
- 有,是很可能有;无,是肯定无
- 使用时最好不要让实际元素数量远大于初始化数量,一次给够避免扩容
- 当实际元素数量超过初始化数量时,应对布隆过滤器进行重建;重新分配更大的过滤器,再将所有的历史元素批量add 进行
使用场景
- 解决缓存穿透问题,Redis结合bitmap使用
- 黑名单校验,识别垃圾邮寄
- 安全连接网址,全球网站判断
- 等。。。
实现布隆过滤器
整体架构
二进制数组构建过程
- 预加载符合条件得记录
- 计算每条记录得hash
- 计算hash值对应得bitmap数组位置
- 修改值

查询元素是否存在得过程
- 计算元素得hash值
- 计算hash值对应二进制数组得位置
- 找到数组中对应位置的值,0 代表不存在,1 代表存在
代码落地
初始化白名单

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
/**
* 初始化布隆过滤器
*
* @author :liao.wei
* @date :2023/9/23 16:13
* @package : com.mco.utils.bloomfilter
*/
@Component
@Slf4j
@RequiredArgsConstructor
public class BloomFilterInitUtil {
public static final String WHITE_CACHE_KEY_CUSTOMER = "white_list_customer";
private final RedisTemplate redisTemplate;
/**
* 初始化工具
*/
@PostConstruct
public void init() {
// 白名单key 加载到过滤器
String key = "customer:2";
// 计算hash值,对计算结果取绝对值(因为存在负值)
int hashVal=Math.abs(key.hashCode());
//计算位数位置(通过hashVal和2的32次方取模后,余数)
long index= (long) (hashVal%Math.pow(2,32));
log.info(key+"-对应的位置-"+index);
//设置redis 中bitmap 对应位置的坑位,设置 1
redisTemplate.opsForValue().setBit(WHITE_CACHE_KEY_CUSTOMER,index,true);
}
}
在Redis 中检查一下是否置为1

检查工具
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
/**
* 布隆过滤器检查工具
*
* @author :liao.wei
* @date :2023/9/23 16:24
* @package : com.mco.utils.bloomfilter
*/
@Component
@Slf4j
@RequiredArgsConstructor
public class CheckUtil {
private final RedisTemplate redisTemplate;
public boolean checkWithBloomFilter(String checkItem, String key) {
// 计算hash值,对计算结果取绝对值(因为存在负值)
int hashVal = Math.abs(key.hashCode());
//计算位数位置(通过hashVal和2的32次方取模后,余数)
long index = (long) (hashVal % Math.pow(2, 32));
log.info(key + "-对应的位置-" + index);
//设置redis 中bitmap 对应位置的坑位,设置 1
return redisTemplate.opsForValue().getBit(checkItem, index);
}
}
应用布隆过滤
//-------------加入布隆过滤器---------------
if (!checkUtil.checkWithBloomFilter(WHITE_CACHE_KEY_CUSTOMER, key)) {
log.info("无此customer");
return null;
}
log.info("进入");
//---------------------------
优缺点
| 优点 | 缺点 |
| 高效插入和查询,内存占用bit空间少 | 不能删除,存在hash冲突,可能造成误删除 |
| 存在误差,不能精确过滤 |
🌹 以上分享 Redis 布隆过滤器实际应用,如有问题请指教。
🌹🌹 如你对技术也感兴趣,欢迎交流。
🌹🌹🌹 如有需要,请👍点赞💖收藏🐱🏍分享
布隆过滤器是一种高效的数据结构,用于判断元素是否可能存在。它利用多个哈希函数将元素映射到位数组中,判断时通过检查位数组来推测元素存在性。虽然存在误判风险,但能有效防止缓存穿透。Redis中的Bitmap同样适用于快速标记状态,常用于黑名单校验、网站判断等场景。实现时需初始化位数组,并根据哈希值设置和检查位。当元素过多时,需考虑重构过滤器。

1344

被折叠的 条评论
为什么被折叠?



