布隆过滤器容量不够用如何解决?
# 回答话术
当系统运行过程中,布隆过滤器中的元素逐渐追平或超过设置的元素数量,就会引起误判率增加的风险。
为此,我们需要一种预警以及容量不够用的解决方案。我觉得可以通过定时任务扫描布隆过滤器的容量,判断当前容量距离最初设置峰值差量还有多少。
假如设置阈值是 20%,当布隆过滤器中元素满 80% 以上后,就触发告警发邮件或其它通信工具。然后人为或程序操作拉起一个新的布隆过滤器,新布隆过滤器的容量建议为旧容量的 1.5 倍,以此解决布隆过滤器容量问题。
因为布隆过滤器并没有数据同步方法,所以历史数据需要从源表(可能是 MySQL 等数据源)中重新读区并写入到新布隆过滤器。
# 问题详解
虽然看着上面的思路以及解决方案没有问题,但是如果要实现,需要具备一些细节难点知识。
# 1. 难点一:如何判断布隆过滤器的容量达到阈值?
Redisson 布隆过滤器 RedissonBloomFilter
实现类中有一个 count 方法,返回的是新增到布隆过滤器的数据。
看到这里你是否会感到疑惑,布隆过滤器这样一个位数组,通过 Hash 新增和查询,怎么可能会有数据统计的功能?
这是因为布隆过滤器底层结构是 BitMap,BitMap 是可以通过 BITCOUNT
命令统计元素数量。
# 2. 如何把历史数据快速同步到新布隆过滤器?
针对大数据量查询,我们可以使用 select * from table where id > xxx limit xxx;
每次查处一批数据后,把最大的 id 当作查询条件赋值给下一次查询。通过这种方式不断循环,直到查出全部数据并放到新布隆过滤器。
为什么不使用常规 limit xx xx
?因为这会存在深分页问题,在大数据量场景下,分页越是到后面越是容易形成性能深渊。
# 3. 定时任务多久执行一次合适?
针对这种较为重要的判断,可以适当让扫描周期密集些,比如 1-5 分钟执行一次。触发设定阈值后,发起报警。
# 4. 如何在代码中替换新旧布隆过滤器?
我们可以假设布隆过滤器从初始就是两个,可以通过一个布尔类型的变量判断使用哪个布隆过滤器。
伪代码如下:
public RBloomFilter<String> getActualBloomFilter() {
return bloomFilterFlag ? oldBloomFilter : newBloomFilter;
}
通过配置中心控制 bloomFilterFlag
参数,即可实现不停机更新。
另外,如果新的布隆过滤器容量又不够了,那我们只需要将旧布隆过滤器删掉重新创建一个更大的,再修改下 bloomFilterFlag
就可以无缝实现该功能。