一、布隆过滤器原理
BloomFilter 的算法是,首先分配一块内存空间做 bit 数组,数组的 bit 位初始值全部设为 0。
加入元素时,采用 k 个相互独立的 Hash 函数计算,然后将元素 Hash 映射的 K 个位置全部设置为 1。
检测 key 是否存在,仍然用这 k 个 Hash 函数计算出 k 个位置,如果位置全部为 1,则表明 key 存在,否则不存在。
二、应用场景
当你遇到数据量大,又需要去重的时候就可以考虑布隆过滤器,
如下场景:
- 解决 Redis 缓存穿透问题(面试重点);
- 邮件过滤,使用布隆过滤器实现邮件黑名单过滤;
- 爬虫爬过的网站过滤,爬过的网站不再爬取;
- 推荐过的新闻不再推荐;
布隆过滤器可以插入元素,但不可以删除已有元素。
布隆过滤器存在一定的误判,并非100%。
三、下载集成
推荐使用 Redis 版本 6.x,最低 4.x 来集成布隆过滤器。RedisBloom模版需要自己单独下载编译安装。
四、Redission 布隆过滤器实战
4.1 添加 Redission 依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.16.7</version>
</dependency>
4.2 Redis 配置
使用 Spring boot 默认的 Redis 配置方式配置 Redission:
spring:
application:
name: redission
redis:
host: 127.0.0.1
port: 6379
ssl: false
4.3 创建布隆过滤器
@Service
public class BloomFilterService {
@Autowired
private RedissonClient redissonClient;
/**
* 创建布隆过滤器
* @param filterName 过滤器名称
* @param expectedInsertions 预测插入数量
* @param falseProbability 误判率
* @param <T>
* @return
*/
public <T> RBloomFilter<T> create(String filterName, long expectedInsertions, double falseProbability) {
RBloomFilter<T> bloomFilter = redissonClient.getBloomFilter(filterName);
bloomFilter.tryInit(expectedInsertions, falseProbability);
return bloomFilter;
}
}
4.4 单元测试
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = RedissionApplication.class)
public class BloomFilterTest {
@Autowired
private BloomFilterService bloomFilterService;
@Test
public void testBloomFilter() {
// 预期插入数量
long expectedInsertions = 10000L;
// 错误比率
double falseProbability = 0.01;
RBloomFilter<Long> bloomFilter = bloomFilterService.create("ipBlackList", expectedInsertions, falseProbability);
// 布隆过滤器增加元素
for (long i = 0; i < expectedInsertions; i++) {
bloomFilter.add(i);
}
long elementCount = bloomFilter.count();
log.info("elementCount = {}.", elementCount);
// 统计误判次数
int count = 0;
for (long i = expectedInsertions; i < expectedInsertions * 2; i++) {
if (bloomFilter.contains(i)) {
count++;
}
}
log.info("误判次数 = {}.", count);
bloomFilter.delete();
}
}
注意事项:如果是 Redis Cluster 集群,则需要
RClusteredBloomFilter<SomeObject> bloomFilter = redisson.getClusteredBloomFilter("sample")