redis布隆过滤器

布隆过滤器

1.布隆过滤器的作用

主要作用: 防止缓存穿透(当redis上不存在的时候,当请求来的时候就会去查询数据库,数量小的时候没事,但是当数据量大的时候,那此时大量的请求压在数据库可能导致数据库崩溃),黑名单设置(垃圾邮件识别)。

2. 构成

布隆过滤器的组成就是一个bit数组 和 多个hash函数构成,当数据来的时候通过hash函数多次求的值,在bit数组上对应位置置为1,就代表特定数据。这时候也就有了问题看图
在这里插入图片描述
比如说A通过hash函数将0,1,2,3,4五个位置的0置为1,B则将2,3,4,6,7置为1 ,这时候可以注意到重复部分2,3,4这时候就引入一个新的概念,就是布隆过滤器不要删除,因为一个bit为并不是独占的,可能多个占用。此时还有一个可能就是例如C正好经过hash时候会映射的位置时1,2,3,4,6但是此时布隆过滤器中并未存储C的相关信息,但是此时这些位置都是1 ,也就产生了误判,也就是布隆过滤器存在误判
总结:一个元素判断结果为没有时则一定没有, 如果判断结果为存在的时候元素不一定存在。
优点:高效地插入和查询,占用空间少
缺点:不能删除元素。因为删掉元素会导致误判率增加,因为hash冲突同一个位置可能存的东西是多个共有的,你删除一个元素的同时可能也把其它的删除了。
存在误判不同的数据可能出来相同的hash值

代码演示

1)使用谷歌提供的方式

        <!--guava Google 开源的 Guava 中自带的布隆过滤器-->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>23.0</version>
        </dependency>
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import java.util.ArrayList;
import java.util.List;

public class GuavaBloomfilterDemo
{
    public static final int _1W = 10000;
    //布隆过滤器里预计要插入多少数据
    public static int size = 100 * _1W;
    //误判率,它越小误判的个数也就越少(思考,是不是可以设置的无限小,没有误判岂不更好,开销会越来越大,建立的bit数组大小)
    public static double fpp = 0.01; //默认值0.03

    public void bloomFilter()
    {
        // 创建布隆过滤器对象
        BloomFilter<Integer> filter = BloomFilter.create(Funnels.integerFunnel(), 100);
        // 判断指定元素是否存在
        System.out.println(filter.mightContain(1));
        System.out.println(filter.mightContain(2));
        // 将元素添加进布隆过滤器
        filter.put(1);
        filter.put(2);
        System.out.println(filter.mightContain(1));
        System.out.println(filter.mightContain(2));

    }

    /**
     * 误判率演示+源码分析
     */
    public void bloomFilter2()
    {
        // 构建布隆过滤器
        BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(),size,fpp);

        //1 先往布隆过滤器里面插入100万的样本数据
        for (int i = 0; i < size; i++) {
            bloomFilter.put(i);
        }
       /* List<Integer> listSample = new ArrayList<>(size);
        //2 这100万的样本数据,是否都在布隆过滤器里面存在?
        for (int i = 0; i < size; i++)
        {
            if (bloomFilter.mightContain(i)) {
                listSample.add(i);
                continue;
            }
        }
        System.out.println("存在的数量:" + listSample.size());*/

        //3 故意取10万个不在过滤器里的值,看看有多少个会被认为在过滤器里,误判率演示
        List<Integer> list = new ArrayList<>(10 * _1W);

        for (int i = size+1; i < size + 100000; i++)
        {
            if (bloomFilter.mightContain(i)) {
                System.out.println(i+"\t"+"被误判了.");
                list.add(i);
            }
        }
        System.out.println("误判的数量:" + list.size());
    }
}

2)使用jrebloom布隆过滤器

        <!-- redisson -->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.13.4</version>
        </dependency>
        <dependency>
            <groupId>com.redislabs</groupId>
            <artifactId>jrebloom</artifactId>
            <version>2.1.0</version>
        </dependency>

import org.redisson.Redisson;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.StringCodec;
import org.redisson.config.Config;

import java.util.concurrent.TimeUnit;


public class RedissonBloomFilterDemo
{
    public static final int _1W = 10000;

    //布隆过滤器里预计要插入多少数据
    public static int size = 100 * _1W;
    //误判率,它越小误判的个数也就越少
    public static double fpp = 0.03;

    static RedissonClient redissonClient = null;//jedis
    static RBloomFilter rBloomFilter = null;//redis版内置的布隆过滤器

    static
    {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.111.147:6379").setDatabase(0);
        //构造redisson
        redissonClient = Redisson.create(config);
        //通过redisson构造rBloomFilter
        rBloomFilter = redissonClient.getBloomFilter("phoneListBloomFilter",new StringCodec());
        rBloomFilter.tryInit(size,fpp);
        // 1测试  布隆过滤器有+redis有
        //rBloomFilter.add("10086");
        //redissonClient.getBucket("10086",new StringCodec()).set("chinamobile10086");
        // 2测试  布隆过滤器有+redis无
        //rBloomFilter.add("10087");
        //3 测试 ,布隆过滤器无+redis无
    }

    private static String getPhoneListById(String IDNumber)
    {
        String result = null;

        if (IDNumber == null) {
            return null;
        }
        //1 先去布隆过滤器里面查询
        if (rBloomFilter.contains(IDNumber)) {
            //2 布隆过滤器里有,再去redis里面查询
            RBucket<String> rBucket = redissonClient.getBucket(IDNumber, new StringCodec());
            result = rBucket.get();
            if(result != null)
            {
                return "i come from redis: "+result;
            }else{
                result = getPhoneListByMySQL(IDNumber);
                if (result == null) {
                    return null;
                }
                // 重新将数据更新回redis
                redissonClient.getBucket(IDNumber, new StringCodec()).set(result);
            }
            return "i come from mysql: "+result;
        }
        return result;
    }

    private static String getPhoneListByMySQL(String IDNumber)
    {
        return "chinamobile"+IDNumber;
    }
    public static void main(String[] args)
    {
        //String phoneListById = getPhoneListById("10086");
        //String phoneListById = getPhoneListById("10087"); //请测试执行2次
        String phoneListById = getPhoneListById("10088");
        System.out.println("------查询出来的结果: "+phoneListById);

        //暂停几秒钟线程
        try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
        redissonClient.shutdown();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值