在一个app的登录场景中,有两个重要的信息:账号和设备号
设备号标识一个被安装的app,在app安装时生成,如果卸载了重新安装则会生成新的设备号。
假设有个需求,需要检测一个时间窗口内活跃账号和设备比是否在合理范围内,如果不在合理范围内则告警提醒。
一般的方案有下面两种:
方案 | 特点 |
---|---|
内存set | 优点:性能好,缺点:存储数据有限 |
数据库 | 优点:可以存储更多数据,缺点:性能相对差,需要维护数据的清除逻辑 |
这两种方案各有其优缺点。
那么,如果我既追求性能,又追求存储更多的数据,有办法吗
于是布隆过滤器就派上用场了。
布隆过滤器的使用也是将数据存储到内存,同时又可以对数据进行压缩,所以能够在保证性能的同时,存储更多的数据。
前提是能容忍一定程度的误判率,而刚好多数业务场景的却不需要那么精准的数据。
如果想在本地内存里维护布隆过滤器,可以使用Guava的实现。
BloomFilter<CharSequence> bloomFilter = BloomFilter.create(
Funnels.stringFunnel(Charsets.UTF_8),
// 期望的插入数据量
100000,
// 期望的容错率
0.0001);
但是本地内存依然是有限的,并且实际场景下,一般是多个应用实例组成的集群需要维护共用的布隆过滤器,所以会考虑使用分布式缓存组件,如redis
若使用redis,也有几种实现方式
-
安装redis布隆过滤器插件 — redisbloom
可自行百度。 -
基于redis的bitmap实现
public class RedisBloomFilter {
// 布隆过滤器的 redis key 前缀 利用它可以统计过滤器对Redis的使用情况
private static final String KEY_PREFIX = "bf:";
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* bit数组长度
*/
private final long numBits;
// hash函数数量
private final int numHashFunctions;
/**
*
* @param expectedInsertions 预计插入量
* @param fpp 可接受的错误率
*/
public RedisBloomFilter(long expectedInsertions, double fpp) {
this.numBits = optimalNumOfBits(expectedInsertions, fpp);