Redis之布隆过滤器

本文介绍了Redis中的布隆过滤器(BloomFilter)及其应用场景。BloomFilter是一个用于去重的高效数据结构,虽然存在误判可能但节省空间。文章详细讲解了BloomFilter的原理、安装、基本用法,并给出了Java客户端操作示例。此外,还讨论了其在防止Redis穿透、缓存击穿问题上的应用。
摘要由CSDN通过智能技术生成

1 问题场景

  1. 我们用HyperLogLog来估计一个数,有偏差但是也够用。HyperLogLog主要提供两个方法:pfadd和pfcount

  2. 但是HyperLogLog没有判断是否包含的方法,例如pfexists、pfcontains等。没有办法统计某个值是否存在,但我们有这个业务需求。

  3. 例如刷今日头条,推送的内容有相似的,但是没有重复的。这就涉及到如何在推送的时候去重?

  4. 解决方案很多,例如将用户的浏览历史记录下来,然后每次推送时去比较该条消息是否已经给用户推送了。但是这种方式效率极低,不推荐。

通过布隆过滤器可以解决该问题

2 Bloom Filter概念介绍

  1. Bloom Filter专门用来解决我们上面所说的去重问题,使用Bloom Filter不会像使用缓存那么浪费空间。当然,他也存在不太精确的问题,不过这个问题可人为控制,要精确就需要占用更多的空间。

  2. Bloom Filter相当于是一个不太精确的set集合,我们可以利用它里面的contains方法去判断某一个对象是否存在,但是需要注意,这个判断不是特别精确。一般来说,通过contains判断某个值不存在,那就一定不存在,但是判断某个值存在的话,则他可能不存在。(就是推送的消息一定是没看过的,但是有可能没看过的漏掉了,没有进行推送)

  3. 以今日头条为例,假设我们将用户的浏览记录用B表示,A表示用户没有浏览的新闻,现在要给用户推送消息,先去B里面判断这条消息是否已经推送过(B里面没有这条记录),那就一定没有推送过,如果判断结果说有推送过(B里边也有可能没有这条消息),这个时候这条消息就不会推送给用户,导致用户错过这条信息,当然这是概率极低,但是存在。

3 原理介绍

每一个布隆过滤器,在Redis中都对应了一个大型的位数组以及几个不同的无偏(计算的数比较均匀)hash函数

所谓的add操作是这样的:

  1. 首先根据几个不同的hash函数给元素进行hash,算出不同一个整数索引值。拿到这个索引值之后,全部对位数组的长度进行取模运算,都会得到一个位置。即每一个hash函数都会得到一个位置,然后在位数组中对应的位置设置为1,这就完成了添加操作。

  2. 下次要查询的时候,则进行相同的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. 假设有1亿条用户数据,现在查询用户要在数据库中查,效率低而去数据库压力大,所以我们会把请求首先在Redis中处理(活跃用户存在Redis中),Redis中没有的用户,再去数据库中查询。
  2. 现在可能会存在一种恶意请求,这个请求携带上了很多不存在的用户,这个时候Redis无法拦截下来请求,所以请求会直接跑到数据库里去。这个时候,这些恶意请求会击穿我们的缓存,甚至数据库,进而引起“雪崩效应”。

为了解决这个问题,我们就可以使用布隆过滤器(可以拦截大部分不存在的用户)。将一亿条用户数据存储Redis中不现实,但是可以存储在布隆过滤器中,请求来了,首先去判断数据是否存在,如果存在,先去Redis处查,Redis查询没有的话,再去数据库中查询;如果不存在的话,就不用去查询。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值