1 问题场景
-
我们用HyperLogLog来估计一个数,有偏差但是也够用。HyperLogLog主要提供两个方法:pfadd和pfcount
-
但是HyperLogLog没有判断是否包含的方法,例如pfexists、pfcontains等。没有办法统计某个值是否存在,但我们有这个业务需求。
-
例如刷今日头条,推送的内容有相似的,但是没有重复的。这就涉及到如何在推送的时候去重?
-
解决方案很多,例如将用户的浏览历史记录下来,然后每次推送时去比较该条消息是否已经给用户推送了。但是这种方式效率极低,不推荐。
通过布隆过滤器可以解决该问题
2 Bloom Filter概念介绍
-
Bloom Filter专门用来解决我们上面所说的去重问题,使用Bloom Filter不会像使用缓存那么浪费空间。当然,他也存在不太精确的问题,不过这个问题可人为控制,要精确就需要占用更多的空间。
-
Bloom Filter相当于是一个不太精确的set集合,我们可以利用它里面的contains方法去判断某一个对象是否存在,但是需要注意,这个判断不是特别精确。一般来说,通过contains判断某个值不存在,那就一定不存在,但是判断某个值存在的话,则他可能不存在。(就是推送的消息一定是没看过的,但是有可能没看过的漏掉了,没有进行推送)
-
以今日头条为例,假设我们将用户的浏览记录用B表示,A表示用户没有浏览的新闻,现在要给用户推送消息,先去B里面判断这条消息是否已经推送过(B里面没有这条记录),那就一定没有推送过,如果判断结果说有推送过(B里边也有可能没有这条消息),这个时候这条消息就不会推送给用户,导致用户错过这条信息,当然这是概率极低,但是存在。
3 原理介绍
每一个布隆过滤器,在Redis中都对应了一个大型的位数组以及几个不同的无偏(计算的数比较均匀)hash函数
所谓的add操作是这样的:
-
首先根据几个不同的hash函数给元素进行hash,算出不同一个整数索引值。拿到这个索引值之后,全部对位数组的长度进行取模运算,都会得到一个位置。即每一个hash函数都会得到一个位置,然后在位数组中对应的位置设置为1,这就完成了添加操作。
-
下次要查询的时候,则进行相同的hash计算,再得出位数组的位置,然后看数组中那个位置是否为1,然后为1的话就是已经存在,不过可能其他的数字多了,占了那个位置,其可能原来不存在。(当判断元素是否存在时,依然先对元素进行hash运算,将运算的结果和位数组取模,然后去对应的位置查看是否有相应的数据,如果有,表示元素可能存在(因为这个有数据的地方也可能是其他元素进来的),如果没有表示元素一定不存在。)
2 6 8
对10取模,得到 2、6、8

位数组越长的话,起冲突的概率很小,越短的话,起冲突的概率越大
Bloom Filter中,误判的概率和位数组的大小有很大的关系,位数组越大,误判概率越小,当然占用的存储空间越大;位数组越小,误判概率越大,当然占用的存储空间就越小。
4 Bloom Filter安装
官网地址:https://oss.redislabs.com/redisbloom/Quick_Start/
第一种:docker
//用Docker启动Redisbloom
docker run -p 6379:6379 --name redis-redisbloom redislabs/rebloom:latest
第二种:自己编译安装:安装在redis目录下
git clone https://github.com/RedisBloom/RedisBloom.git
cd RedisBloom
make
root@VM-190-15-centos redis-5.0.7]# redis-cli -a password
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> shutdown
not connected> exit
配置的时候需要先进入redis并进行关闭
在redis的目录下,执行该命令,启动,这个时候是前台启动
redis-server --loadmodule ./RedisBloom/redisbloom.so
加redis.conf后,这样就后台启动了
redis-server redis.conf --loadmodule ./RedisBloom/redisbloom.so
该命令可使用,表示布隆过滤器安装好了
127.0.0.1:6379> bf.add k1 v1
(integer) 1
每次启动时都输入下面这句比较麻烦,可以在配置文件redis.conf里面进行配置
redis-server redis.conf --loadmodule ./RedisBloom/redisbloom.so
添加下面这句
loadmodule /root/redis-5.0.7/RedisBloom/redisbloom.so
然后以后启动redis-server redis.conf即可拥有布隆过滤器的功能
5 基本用法
Bloom Filter不支持删除操作,主要是两类命令,添加和判断是否存在。
- bf.add\bf.madd 添加和批量添加
- bf.exists\bf.mexists 判断是否存在和批量判断是否存在
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> bf.add k1 v1
(integer) 1
127.0.0.1:6379> bf.exists k1 v1
(integer) 1
127.0.0.1:6379> bf.exists k1 v2
(integer) 0
127.0.0.1:6379> bf.madd k1 v2 v3 v4
1) (integer) 1
2) (integer) 1
3) (integer) 1
127.0.0.1:6379> bf.mexists k1 v1 v2 v3 v4 v5
1) (integer) 1
2) (integer) 1
3) (integer) 1
4) (integer) 1
5) (integer) 0
使用Jedis操作布隆过滤器:
添加布隆过滤器依赖:不过要在jedis3以上才支持
<dependency>
<groupId>com.redislabs</groupId>
<artifactId>jrebloom</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
java客户端代码测试:
public class BloomFilter {
public static void main(String[] args) {
GenericObjectPoolConfig config=new GenericObjectPoolConfig();
//连接池最大空闲数
config.setMaxIdle(300);
//最大连接数
config.setMaxTotal(1000);
//连接最大等待时间,如果是-1表示没有限制
config.setMaxWaitMillis(30000);
//空闲时检查有效性
config.setTestOnBorrow(true);
/**
* 1.redis地址
* 2.redis
* 3.连接超时时间
* 4.密码
*/
JedisPool pool = new JedisPool(config, "127.0.0.1", 6379, 30000, "password");
Client client = new Client(pool);
//存入数据
for (int i = 0; i <10000; i++) {
client.add("name","java"+i);
}
//检查数据是否存在
System.out.println(client.exists("name","java9"));
System.out.println(client.exists("name","java10001"));
}
}
默认情况下,我们使用的布隆过滤器的错误率是0.01,默认的元素大小是100。这两个参数可以配置
bf.reserve进行对k1配置
127.0.0.1:6379> bf.reserve k1 0.0001 1000
OK
第一个参数是key,第二个参数是错误率,错误率越低,占用的空间越大,第三个参数是预计存储的数量(实际上存储可以超出限制),当实际数量超出预计数量时,错误率会上升。
6 典型场景
前面所说的新闻推送过滤算是一个引用场景。
解决Redis穿透或者又叫缓存击穿问题
- 假设有1亿条用户数据,现在查询用户要在数据库中查,效率低而去数据库压力大,所以我们会把请求首先在Redis中处理(活跃用户存在Redis中),Redis中没有的用户,再去数据库中查询。
- 现在可能会存在一种恶意请求,这个请求携带上了很多不存在的用户,这个时候Redis无法拦截下来请求,所以请求会直接跑到数据库里去。这个时候,这些恶意请求会击穿我们的缓存,甚至数据库,进而引起“雪崩效应”。
为了解决这个问题,我们就可以使用布隆过滤器(可以拦截大部分不存在的用户)。将一亿条用户数据存储Redis中不现实,但是可以存储在布隆过滤器中,请求来了,首先去判断数据是否存在,如果存在,先去Redis处查,Redis查询没有的话,再去数据库中查询;如果不存在的话,就不用去查询。
本文介绍了Redis中的布隆过滤器(BloomFilter)及其应用场景。BloomFilter是一个用于去重的高效数据结构,虽然存在误判可能但节省空间。文章详细讲解了BloomFilter的原理、安装、基本用法,并给出了Java客户端操作示例。此外,还讨论了其在防止Redis穿透、缓存击穿问题上的应用。

1013

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



