前段时间和小伙伴讨论 redis 在实际生产中的应用,提到了布隆过滤器 。于是做了一些功课。
1 布隆过滤器简介
布隆过滤器(Bloom Filter)是一个高空间利用率的概率性数据结构,由二进制向量(即位数组)和一系列随机映射函数(即哈希函数)两部分组成。如下所示:
添加元素
当使用布隆过滤器添加 key 时,会使用不同的 hash 函数对 key 存储的元素值进行哈希计算,从而会得到多个哈希值。根据哈希值计算出一个整数索引值,将该索引值与位数组长度做取余运算,最终得到一个位数组位置,并将该位置的值变为 1。每个 hash 函数都会计算出一个不同的位置,然后把数组中与之对应的位置变为 1。通过上述过程就完成了元素添加(add)操作。
判断元素是否存在
当我们需要判断一个元素是否存时,其流程如下:首先对给定元素再次执行哈希计算,得到与添加元素时相同的位数组位置,判断所得位置是否都为 1,如果其中有一个为 0,那么说明元素不存在,若都为 1,则说明元素有可能存在。
误判
那些被置为 1 的位置也可能是由于其他元素的操作而改变的。比如,元素1 和 元素2,这两个元素同时将一个位置变为了 1。在这种情况下,我们就不能判定“元素 1”一定存在,这是布隆过滤器存在误判的根本原因。
2 布隆过滤器使用场景
1 原本有10亿个号码,现在又来了10万个号码,要快速准确判断这10万个号码是否在10亿个号码库中?
2 接触过爬虫的,应该有这么一个需求,需要爬虫的网站千千万万,对于一个新的网站url,我们如何判断这个url我们是否已经爬过了?
3 垃圾邮箱的过滤,黑白名单过滤
4 多用于处理缓存穿透问题
3 布隆过滤器安装与使用
1 docker 安装
docker pull redislabs/rebloom:latest
docker run -p 6379:6379 --name redis-redisbloom redislabs/rebloom:latest
docker exec -it redis-redisbloom bash
redis-cli
#测试是否安装成功
127.0.0.1:6379> bf.add test hello
2 直接编译安装
建议使用 1.1.1 版本
下载地址:
https://github.com/RedisBloom/RedisBloom/releases/tag/v1.1.1
可以直接下载编译好的 rebloom.so 文件
拷贝至指定目录:
cp rebloom.so /data/redis/conf/
在redis配置文件里加入以下配置:
loadmodule /data/redis/conf/rebloom.so
配置完成后重启redis服务:
#测试是否安装成功
127.0.0.1:6379> bf.add test hello
4 常用命令
命令 | 说明 |
bf.add | 只能添加元素到布隆过滤器。 |
bf.exists | 判断某个元素是否在于布隆过滤器中。 |
bf.madd | 同时添加多个元素到布隆过滤器。 |
bf.mexists | 同时判断多个元素是否存在于布隆过滤器中。 |
bf.reserve | 以自定义的方式设置布隆过滤器参数值,共有 3 个参数分别是 key、error_rate(错误率)、initial_size(初始大小)。 |
5 Redission 使用布隆过滤器
/**
* 操作布隆过滤器
*
* @param name
* @return
*/
public RBloomFilter<String> getBloomFilter(String name) {
return redissonClient.getBloomFilter(name);
}
@Test
public void testBloomFilter() {
RBloomFilter<String> demo = component.getBloomFilter("demo");
demo.tryInit(10000L, 0.01);
for (int i = 0; i < 10000; i++) {
demo.add(String.valueOf(i));
}
// 误判次数
int count = 0;
for (int i = 10000; i < 20000; i++) {
if (demo.contains(String.valueOf(i))) {
count++;
}
}
System.out.println(count);
}
ps:实际操作过程中发现,redisson 并没有使用到布隆过滤器的插件,而是采用 hash 和 string 来实现的。