精华内容
下载资源
问答
  • PHP + Redis 实现布隆过滤器,当你的项目需要有大并发的时候,比如id是有序的int类型的时候,增加布隆过滤器可以防止缓存失效直接查询数据库导致的缓存穿透
  • 在介绍布隆过滤器之前我们首先引入几个场景。 场景一 在一个高并发的计数系统中,如果一个key没有计数,此时我们应该返回0,但是访问的key不存在,相当于每次访问缓存都不起作用了。那么如何避免频繁访问数量为0的...
  • 布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。这篇文章主要介绍了Python+Redis实现布隆过滤器,需要的朋友可以参考下
  • 布隆过滤器优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。本文将介绍布隆过滤器的原理以及Redis如何实现布隆过滤器,感兴趣的朋友跟随小编一起看看吧
  • redis布隆过滤器

    千次阅读 2020-02-19 13:13:36
    # 布隆过滤器 ### 定义 布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。 ### 用处 布隆过滤器可以用于检索一个元素是否在一个集合中。具体使用有: ...

    布隆过滤器

    定义

    布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。

    用处

    布隆过滤器可以用于检索一个元素是否在一个集合中。具体使用有:

    1. 网页爬虫对URL的去重,避免爬取相同的URL地址

    2. 反垃圾邮件,从数十亿个垃圾邮件列表中判断某邮箱是否垃圾邮箱(同理,垃圾短信)

    3. 缓存穿透,将所有可能存在的数据缓存放到布隆过滤器中,当黑客访问不存在的缓存时迅速返回避免缓存及DB挂掉

    关于缓存穿透:

    我们平时为了优化业务查询效率,通常会选择诸如redis一类的缓存, 数据查询时如果缓存有则直接通过缓存拿取,没有或者key过期的话,则去找数据库. 找到之后再把数据加入到缓存. 如果有这样的一个场景,有用户大量请求不存在的数据id, 这个时候, 因为缓存

    没有,则统统全甩个数据库,这样很可能导致数据库宕掉.同时数据全都直接由持久层获得, 缓存命中率参数失去了意义,缓存也失去了意义.这类情况,称之为缓存穿透.

    优点

    它的优点是空间效率和查询时间都比一般的算法要好的多

    缺点

    它的缺点是有一定的误识别率和删除困难,但是瑕不掩瑜,他的优点足以让我们选择它作为提高查询性能的工具.

    原理

    布隆过滤器内部维护一个全为0的bit数组,需要说明的是,布隆过滤器有一个误判率的概念,误判率越低,则数组越长,所占空间越大。误判率越高则数组越小,所占的空间越小。

    因为是bit数组,不是0 就是 1 , 这里我们初始化一个16位全0的数组:

    这里为简化情况便于理解,我们设定hash函数个数为3 ,分别为 hash1(),hash2(),hash3()

    bit数组长度arrLength为16位

    对数据 data1, 分别使用 三个函数对其进行hash, 这里举例hash1(), 其他两个都是相同的

    hashX(data1),通过hash算法和二进制操作, 然后  处理后的哈希值 % arrLentgh,得到在数组的下标 ,假设 下标 = 3,

    如图我们将数组下标置为1:

    同理,假设 3个函数处理完后如下图:

    这样,花费很少的空间,就能够存储这条数据的存在情况, 当同样的数据请求过来,因为hash函数的特性, 三个函数hash过后,

    通过判断三个比特位是否都是1,就可知道是否是同一条数据(???)

    那么,情况真的这么简单吗?

    其实,布隆过滤器有这样一个特性,那就是: 如果所有位都重复不代表是重复数据,如果有哪怕一位不重复,则肯定不是重复数据

    因为hash值相同,不一定是相同数据,这个好理解吧?

    而hash值不同,肯定不是相同数据. 因此,我们知道,布隆过滤器对于是否重复的判断,是有着误判率的.这一点我们需要了解.

    实现

    实现方式1:  谷歌guaua框架(这方面请读者自行百度一下)

    实现方式2: 借助redis

    代码如下:

    package com.example.demo.test;
    
    import com.google.common.hash.Funnels;
    import com.google.common.hash.Hashing;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import redis.clients.jedis.Jedis;
    
    import java.nio.charset.Charset;
    import java.util.HashMap;
    import java.util.Map;
    
    
    /**
     * redis布隆过滤器 (布隆过滤器规则: 如果所有位都重复不代表是重复数据,如果有哪怕一位不重复,则肯定不是重复数据)
     * <p>
     * 新增数据处理后id填充布隆过滤器(得HASH,设置bitmap的位) ->
     * 当新的请求来对比id , 看看是不是能在布隆过滤器中找到重复数据 ->
     * true:判定为重复数据则进缓存找,如果没有,则是系统误判, 此时进入数据库
     * false: 判定为非重复数据则直接进数据库
     */
    
    public class RedisBloomFilter {
        static final int expectedInsertions = 100;//要插入多少数据
        static final double fpp = 0.01;//期望的误判率
    
        //bit数组长度
        private static long numBits;
    
        //hash函数数量
        private static int numHashFunctions;
    
        private static Jedis jedis = new Jedis("127.0.0.1", 6379);
        private static Map<String,Object> map = new HashMap<>();
        static {
            numBits = optimalNumOfBits(expectedInsertions, fpp);
            numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, numBits);
            //数据模拟0(对象,需要用到序列化知识,篇幅过长,大家自己尝试一下)
            //map.put("10000001", new Goods("10000001","雕牌洗衣粉",6.25,"洗衣粉" ));
            //map.put("10000002", new Goods("10000002","小米空调",3006,"小米空调" ));
            //map.put("10000003", new Goods("10000003","任天堂switch",1776.99,"任天堂switch" ));
            //map.put("10000004", new Goods("10000004","联想笔记本电脑",6799,"联想笔记本电脑" ));
    
            //数据模拟1(这里只缓存价格)
            map.put("10000001", 6.25);
            map.put("10000002", 3006);
            map.put("10000003", 1776.99);
            map.put("10000004", 6799);
        }
    
        public static void main(String[] args) {
            
            //模拟入缓存的数据
            map.forEach((k,v)->{
                jedis.set(k, String.valueOf(v));
                long[] indexs = getIndexs(String.valueOf(k));
                for (long index : indexs) {
                    jedis.setbit("codebear:bloom", index, true);
                }
    
            });
            
            //模拟用户请求的数据
            String userInput1 = "10000001";
            String userInput2 = "10000005";
            String[] arr = {userInput1, userInput2};
            for (int j = 0; j < arr.length; j++) {
                boolean repeated = true;
                long[] indexs = getIndexs(String.valueOf(arr[j]));
                for (long index : indexs) {
                    Boolean isContain = jedis.getbit("codebear:bloom", index);
                    if (!isContain) {
                        System.out.println(arr[j] + "肯定没有重复!");
                        repeated = false;
                        //从数据库获取数据
                        String retVal = getByDb(arr[j]);
                        System.out.println("数据库获取到的数据为"+retVal);
                        break;
                    }
                }
                if (repeated) {
                    System.out.println(arr[j] + "有重复!");
                    //尝试从缓存获取
                    String retVal = getByCache(arr[j]);
                    if (retVal == null) {
                        //从数据库获取
                        retVal = getByDb(arr[j]);
                        System.out.println("数据库获取到的数据为"+retVal);
                        break;
    
                    }
                    System.out.println("缓存获取到的数据为"+retVal);
    
                }
            }
            
        }
    
    
        /**
         * 从缓存获取数据
         */
        public static String getByCache(String key){
            return jedis.get(key);
        }
    
        /**
         * 从数据库获取数据
         */
        public static String getByDb(String key){
            //从数据库获取数据逻辑没有实现
            return "";
        }
    
        /**
         * 根据key获取bitmap下标
         */
        private static long[] getIndexs(String key) {
            long hash1 = hash(key);
            long hash2 = hash1 >>> 16;
            long[] result = new long[numHashFunctions];
            for (int i = 0; i < numHashFunctions; i++) {
                long combinedHash = hash1 + i * hash2;
                if (combinedHash < 0) {
                    combinedHash = ~combinedHash;
                }
                result[i] = combinedHash % numBits;
            }
            return result;
        }
    
        private static long hash(String key) {
            Charset charset = Charset.forName("UTF-8");
            return Hashing.murmur3_128().hashObject(key, Funnels.stringFunnel(charset)).asLong();
        }
    
        //计算hash函数个数
        private static int optimalNumOfHashFunctions(long n, long m) {
            return Math.max(1, (int) Math.round((double) m / n * Math.log(2)));
        }
    
        //计算bit数组长度
        private static long optimalNumOfBits(long n, double p) {
            if (p == 0) {
                p = Double.MIN_VALUE;
            }
            return (long) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));
        }
    }

     

     
     
    展开全文
  • 什么是『布隆过滤器布隆过滤器是一个神奇的数据结构,可以用来判断一个元素是否在一个集合中。很常用的一个功能是用来去重。在爬虫中常见的一个需求:目标网站 URL 千千万,怎么判断某个 URL 爬虫是否宠幸过?...
  • 首先我们先了解,布隆过滤器存在的主要存在意义:在极端处理数据量下(海量数据)仍旧保持一种可用的集合判断方案。 结构构造 位(Bit):即比特,非0即1,是一个二进制数据单位。那么我们判断集合中的数据是否存在也...

    前言

    首先我们先了解,布隆过滤器存在的主要存在意义:在极端处理数据量下(海量数据)仍旧保持一种可用的集合判断方案。 

    结构构造

    位(Bit):即比特,非0即1,是一个二进制数据单位。那么我们判断集合中的数据是否存在也可以用这么个“是否”的单位来存储,类似的在Redis中有Bitmap数据类型:Redis(四)Redis的三种特殊数据类型:GEO、Hyperloglog和Bitmap

    这里举个例子: 0-9十个数字,我们有一百个数字(0-9),随机抽出,如何快速判断凑齐了0-9,建立一个十个位置来存放0-9十个数字(数组),当空位置遇到符合数据放进去,重复数据则不操作。这是不是很像 0 到 1 到 1 的过程呢?每个对应数字都存在一个对应的位置,其实这里就是没有哈希函数的哈希数组。

    hash数组(散列表、哈希表)通过特殊的hash函数映射位置,比如说:365天中每天为一个Bit(当天是否打卡),一个字节8bit,只需要几十个字节!可以理解为值被解析为存放bit的内存地址(散列地址)。

    布隆过滤器

    布隆过滤器实则就是这样:但是为了将数据项添加到布隆过滤器中,布隆过滤器会提供 K 个不同的哈希函数,将值解析结果位置上对应位的值置为 “1”。

    关于误判

    设为3个hansh函数,136分别为为“小明” 的hansh地址,247为小红,那么小白“137”就会被误判。虽然会有误报,但是不存在漏报,即某个元素在某个集合中,肯定能报出来。

    实例代码

    引入依赖

    		<!--布隆过滤器 -->
    		<dependency>
    			<groupId>com.google.guava</groupId>
    			<artifactId>guava</artifactId>
    			<version>25.1-jre</version>
    		</dependency>

    demo

    public class BloomFilterDemo {
    
    	private static int size = 100;
    
    	private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size);
    
    	public static void main(String[] args) {
    		
    		int randomNum;
    
    		for (int i = 0; i < size; i++) {
    			randomNum = (int) (Math.random()*100);
    			bloomFilter.put(randomNum);
    		}
    
    		long startTime = System.nanoTime(); // 获取开始时间
    		int count = 0;
    		for (int i = 0; i < size; i++) {
    			/*
    			 * ns(nanosecond):纳秒, 时间单位。一秒的10亿分之一,即等于10的负9次方秒。常用作 内存读写速度的单位。
    			 * 1纳秒=0.000001 毫秒
    			 * 1纳秒=0.00000 0001秒
    			 */
    			
    			if (bloomFilter.mightContain(i)) {
    				count++;
    				System.out.println("命中了" + i);
    			}
    		}
    		long endTime = System.nanoTime(); // 获取结束时间
    		System.out.println("共命中:" + count + "次 , 程序运行时间: " + (endTime - startTime) + "纳秒" + " , 等价于  " + TimeUnit.MILLISECONDS.convert(endTime - startTime, TimeUnit.NANOSECONDS) + " 毫秒 ||  "
    		+ TimeUnit.SECONDS.convert(endTime - startTime, TimeUnit.NANOSECONDS) +"秒" );
    	}
    
    	
    }

    误判

    误判实例

    size设置为1000000,判断区间1000000 - 2000000,修改代码:

    public class BloomFilterDemo {
    
    	private static int size = 1000000;
    
    	private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size ,0.0001);
    
    	public static void main(String[] args) {
    		
    		int randomNum;
    
    		for (int i = 0; i < size; i++) {
    			randomNum = (int) (Math.random()*size);
    			bloomFilter.put(randomNum);
    		}
    
    		long startTime = System.nanoTime(); // 获取开始时间
    		int count = 0;
    		for (int i = size; i < size * 2; i++) {
    			if (bloomFilter.mightContain(i)) {
    				count++;
    				System.out.println("命中了" + i);
    			}
    		}
    		long endTime = System.nanoTime(); // 获取结束时间
    		System.out.println("共命中:" + count + "次 , 程序运行时间: " + (endTime - startTime) + "纳秒" + " , 等价于  " + TimeUnit.MILLISECONDS.convert(endTime - startTime, TimeUnit.NANOSECONDS) + " 毫秒 ||  "
    		+ TimeUnit.SECONDS.convert(endTime - startTime, TimeUnit.NANOSECONDS) +"秒" );
    		System.err.println("误判次数:" + count + "  误判概率:" + (double)count/size );
    	}

    设置误判率

    设置误判率:默认为 0.03:0.03


     BloomFilter<CharSequence> bf = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), total, 0.0001); 

    其结果:

    1e-6表示1乘以10的负6次方:0.000001。

    Redis使用布隆过滤器

    前面我们讲到了bitmap这种特殊的redis数据结构,他其实就是redis实现布隆过滤器的底层结构,java中使用可以引入Redisson来实现。

    Redission依赖

    		<!--Redisson -->
    		<dependency>
    			<groupId>org.redisson</groupId>
    			<artifactId>redisson</artifactId>
    			<version>2.7.0</version>
    		</dependency>

    demo

    public class RedissonBloomFilter {
    	public static void main(String[] args) {
    		
            Config config = new Config();
            config.useSingleServer().setAddress("127.0.0.1:6379");
            //config.useSingleServer().setPassword("");
            //构造Redisson对象
            RedissonClient redisson = Redisson.create(config);
    
            //构造redis的布隆过滤器
            RBloomFilter<String> bloomFilter = redisson.getBloomFilter("redisBloomFilter");
            
            //初始化布隆过滤器:预计元素为100000000L,误差率为0.03(google的默认值)
            bloomFilter.tryInit(100000000L,0.03);
            
            //插入测试数据到布隆过滤器中
            bloomFilter.add("hello world");
    
            //判断下面号码是否在布隆过滤器中
            System.err.println(bloomFilter.contains("cbry"));    //false
            System.err.println(bloomFilter.contains("hello world"));    //true
        }
    }

    PS

    无密码不需要设置密码,设置空串会报下面错误。 

    Exception in thread "main" org.redisson.client.RedisConnectionException: Can't init enough connections amount! Only 0 from 5 were initialized. Server: /127.0.0.1:6379

    小结

    布隆过滤器虽然可以设置误判率,但是是以牺牲存储空间为代价的。所以不是越小就真的越好。

    展开全文
  • Redis布隆过滤器

    2019-10-07 10:00:00
    Redis布隆过滤器不是原生自带的,而是要通过module加载进去。Redis在4.0的版本中加入了module功能。具体使用可以直接看 RedisBloom  github的README:https://github.com/RedisBloom/RedisBloom。上面有docker...

    点击上方“xy的技术圈”,选择“设为星标

    认真写文章,用心做分享。

    微信公众号:xy的技术圈

    个人网站:yasinshaw.com

    正文

    场景

    在项目开发中,我们经常会遇到去重问题。比如:判断一个人有没有浏览过一篇文章,判断一个人当天是否登录过某个系统,判断一个ip是否发过一个请求,等等。

    比较容易想到的是使用set来实现这个功能。但如果数据量较大,使用set会非常消耗内存,性能也不高。在前面的文章中,我们介绍了一种数据结构:BitMap来提高性能。但BitMap仍然比较消耗内存,尤其是在数据比较稀疏的情况下,使用BitMap并不划算。

    实际上,对于“去重”问题,业界有另外一个更优秀的数据结构来解决这类问题,那就是——布隆过滤器(BloomFilter)。

    原理

    布隆过滤器与BitMap类似,底层也是一个位数组。1表示有,0表示无。但布隆过滤器比BitMap需要更少的内存,它是怎么办到的呢?答案是多个hash。

    我们知道hash算法,是把一个数从较大范围的值,映射到较小范围值。比如我们有一个10位的数组,使用某个hash算法及其数组上的表示:

    hash(“xy”) = 3;

    hash(“技术圈”) = 5;

    0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0

    这样,我们使用这个hash算法就能快速的判断一个字符串是不是存在一个集合里面了。但众所周知,hash算法是有可能发生hash冲突的。比如可能有两个不同的字符串映射到同一个数:

    hash(“xy”) = 3;

    hash(“xy的技术圈”) = 3;

    这种情况下,就不能准确得判断出某个字符串是不是存在于集合之中呢。

    那怎么解决这个问题呢?答案是使用多个不同的hash算法。比如:

    h1(“xy”) = 3, h2(“xy”) = 5, h3(“xy”) = 7;

    h1(“技术圈”) = 5, h2(“技术圈”) = 6, h3(“技术圈”) = 7;

    h1(“xy的技术圈”) = 3, h2(“xy的技术圈”) = 6, h3(“xy的技术圈”) = 9;

    最开始,集合里没有元素,所有位都是0:

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0

    然后,插入“xy”,利用多次hash,把每次hash的结果下标3, 5, 7都插入到相应的地方:

    0, 0, 0, 1, 0, 1, 0, 1, 0, 0

    然后,插入“技术圈”,利用多次hash,把每次hash的结果下标5, 6, 7都插入到相应的地方,已经是1的下标不变:

    0, 0, 0, 1, 0, 1, 1, 1, 0, 0

    这个时候,如果想要判断“xy”是否在集合中,只需要使用同样的3个hash算法,来计算出下标是3, 5, 7,发现这3个下标都为1,那么就认为“xy”这个字符串在集合中。而“xy的技术圈”计算出来的下标是3, 6, 9。发现这三个下标有不是1的地方,比如下标为9的地方是0,那就说明“xy的技术圈”这个字符串还不在集合中。

    误差

    从原理可以看得出来,布隆过滤器是有可能存在一定的误差的。尤其是当hash函数比较少的时候。布隆过滤器是根据多次hash计算下标后,数组的这些下标是否都为1来判断这个元素是否存在的。所以是存在一定的几率,要检查的元素实际上没有插入,但被其它元素插入影响,导致所有下标都为1。

    所以布隆过滤器不能删除,因为一旦删除(即将相应的位置为0),就很大可能会影响其他元素。

    如果使用布隆过滤器判断一个函数是否存在于一个集合,如果它返回true,则代表可能存在。如果它返回false,则代表一定不存在。

    由此可见,布隆过滤器适合于一些需要去重,但不一定要完全精确的场景。比如:

    • 判断一个用户访问了一篇文章

    • 判断一个ip访问了本网站

    • 判断一个key是否被访问过

    相应的,布隆过滤器不适合一些要求零误差的场景,比如:

    • 判断一个用户是否收藏了一篇文章

    • 判断一个用户是否订购了一个课程

    使用技巧

    这就是布隆过滤器的基本原理。由上面的例子可以看出来,如果空间越大,hash函数越多,结果就越精确,但空间效率和查询效率就会越低。

    这里有一个测试数据:

    后面4列中的数据就是发生误差的数量。可见,空间大小和集合大小不变的情况下,增加hash函数可以显著减小误差。但一旦集合大小达到空间大小的25%左右后,增加hash函数带来的提神效果并不明显。这个时候应该增加空间大小。

    Redis中的布隆过滤器

    Redis的布隆过滤器不是原生自带的,而是要通过module加载进去。Redis在4.0的版本中加入了module功能。具体使用可以直接看RedisBloom github的README:https://github.com/RedisBloom/RedisBloom。上面有docker一键启动命令,可以很方便地实验。也有几种主流语言的客户端库的链接,比如Java语言的JReBloom。有兴趣的朋友可以自行了解。

    Redis的布隆过滤器主要有两个命令:

    • bf.add 添加元素到布隆过滤器中:bf.add strs xy

    • bf.exists 判断某个元素是否在过滤器中:bf.exists strs xy

    Redis中有一个命令可以来设置布隆过滤器的准确率:

    bf.reserve strs 0.01 100
    

    三个参数的含义:

    • 第一个值是过滤器的名字。

    • 第二个值为error_rate的值:允许布隆过滤器的错误率。

    • 第三个值为initial_size的值:初始化位数组的大小。

    扩展学习

    Java实现的布隆过滤器

    如果你的项目没有使用Redis,那可以使用一些开源库,基于代码实现,直接存放在内存。比如Google的guava包中提供了BloomFilter类,有兴趣的读者可以去了解一下,研究研究源码和使用。

    布谷鸟过滤器

    RedisBloom模块还实现了布谷鸟过滤器,它算是对布隆过滤器的增强版。解决了布隆过滤器的一些比较明显的缺点,比如:不能删除元素,不能计数等。除此之外,布谷鸟过滤器不用使用多个hash函数,所以查询性能更高。除此之外,在相同的误判率下,布谷鸟过滤器的空间利用率要明显高于布隆,空间上大概能节省40%多。

    笔者个人觉得,对于大多数场景来说,布隆过滤器足以解决我们的问题。掘金上有一篇深度分析布谷鸟过滤器的文章,有兴趣的读者可以去了解一下:https://juejin.im/post/5cfb9c74e51d455d6d5357db。

    END

    推荐阅读

    ↓↓↓ 点击"阅读原文" 去【评论】吧

    展开全文
  • 布隆过滤器 本质上布隆过滤器是一种数据结构,可以用来告诉你 “某样东西一定不存在或者可能存在”。 如果我们平时常用的List,set,map ,tree 来实现同样的效果,set和map都是采用map的数据结构,时间复杂度是O1级别...

    布隆过滤器

    本质上布隆过滤器是一种数据结构,可以用来告诉你 “某样东西一定不存在或者可能存在”。
    如果我们平时常用的List,set,map ,tree 来实现同样的效果,set和map都是采用map的数据结构,时间复杂度是O1级别。但是map 需要保存所有存在的数据,当数据量非常大的时候,消耗的内存是非常大的。布隆过滤器可以极大减小这种内存消耗,但同样会造成部分不存在的数据,误判为存在。可以通过底层维护的bit数组的长度,来调整数据的误判率。

    布隆过滤器是一个 bit 向量或者说 bit 数组:
    当我们输入 “线性代数” ,通过计算3个不同的hash计算公式,将值映射到1,3,5这个几个数组上面去,当我们输入 “线性代数”这 个值,映射到1,3,5上面的时候,发现这几个位置都被映射过了,就可以判断 线性代数 代数可能存在。
    在这里插入图片描述

    当我们再输入 “高等数学” ,通过计算3个不同的hash计算公式,将值映射到1,8,9这个几个数组上面去,这个时候 1,3,5,8,9这几个位置都有被映射过了。

    在这里插入图片描述
    如果我们判断 “概率论” 是否存在的时候,将值进行三种hash运算映射到1,2,3 。发现2没有被映射过,所以可以判断 “概率论” 不存在。 但是也有可能当输入“线性代数” 到映射到1,5,8这三个位置都被映射过了,此时发生误判,认为 “线性代数” 可能存在。

    利用布隆过滤器减少磁盘 IO 或者网络请求,因为一旦一个值必定不存在的话,我们可以不用进行后续昂贵的查询请求。一般运用场景防止缓存穿透,对垃圾邮件、短信的过滤,推荐非重复消息。

    布隆过滤器中的google过滤器相对于redis布隆过滤器有以下的缺点
    基于JVM内存的一种布隆过滤器
    重启即失效
    本地内存无法用在分布式场景
    不支持大数据量存储

    redis布隆 也有相对于google过滤器,需要依赖redis,性能稍差的缺点。

    redis布隆过滤器的安装

    其中添加过滤器依赖于redis单机部署redis集群(4.0.14版本)
    1.下载redisbloom插件(redis官网下载即可)

    https://github.com/RedisLabsModules/redisbloom/

    这里使用v1.1.1版本

    //新建目录
    mkdir redisbloom
    
    cd redisbloom
    
    //下载redis 插件
    wget https://github.com/RedisLabsModules/rebloom/archive/v1.1.1.tar.gz
    
    //解压文件
    tar -zxvf v1.1.1.tar.gz
    
    //进入解压后的目录
    cd RedisBloom-1.1.1/
    
    //编译
    make
    

    在这里插入图片描述

    //进入集群目录,添加redis布隆过滤器的插件
    cd /usr/local/cluster/7001/redis-4.0.14
    
    vi redis.conf 
    cd /usr/local/cluster/7002/redis-4.0.14
     vi redis.conf
     .......
    
    
    

    在redis中找到

    ################################## MODULES #####################################
    
    # Load modules at startup. If the server is not able to load modules
    # it will abort. It is possible to use multiple loadmodule directives.
    #
    # loadmodule /path/to/my_module.so
    # loadmodule /path/to/other_module.so
    #添加 布隆过滤插件
    loadmodule /usr/local/cluster/redisbloom/RedisBloom-1.1.1/rebloom.so
    
    

    在这里插入图片描述

    重启redis

     ./stop.sh
    ./start.sh
    
    

    单机版直接启动

    ./redis-server ../redis.conf
    

    在这里插入图片描述

    //进入到redis 7000节点
    ./redis-cli -p 7000 -c
    
    //添加过滤器值
    192.168.25.128:7001> bf.add calvinBloom 111
    (integer) 1
    192.168.25.128:7001> bf.add calvinBloom 222
    (integer) 1
    192.168.25.128:7001> bf.add calvinBloom 333
    (integer) 1
    192.168.25.128:7001> bf.exists calvinBloom 111
    (integer) 1
    192.168.25.128:7001> bf.exists calvinBloom 222
    (integer) 1
    192.168.25.128:7001> bf.exists calvinBloom 333
    

    三、布隆过滤器的使用

    1、引入依赖

          <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
            <dependency>
                <groupId>redis.clients</groupId>
                <artifactId>jedis</artifactId>
                <version>3.1.0</version>
            </dependency>
    
    
            <dependency>
                <groupId>com.redislabs</groupId>
                <artifactId>jrebloom</artifactId>
                <version>1.2.0</version>
            </dependency>
    

    2、加入redis.properties配置文件

    #客户端超时时间单位是毫秒 默认是2000
    redis.timeout=10000
    #最大空闲数
    redis.maxIdle=300
    #连接池的最大数据库连接数。设为0表示无限制,如果是jedis 2.4以后用redis.maxTotal
    #redis.maxActive=600
    #控制一个pool可分配多少个jedis实例,用来替换上面的redis.maxActive,如果是jedis 2.4以后用该属性
    redis.maxTotal=2000
    #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
    redis.maxWaitMillis=1000
    
    redis.nodes=192.168.25.128:7000,192.168.25.128:7001,192.168.25.128:7002,192.168.25.128:7003,192.168.25.128:7004,192.168.25.128:7005,192.168.25.128:7006,192.168.25.128:7007
    
    
    

    3、引入redis的配置文件

    @Configuration
    @PropertySource("classpath:conf/redis.properties")
    public class RedisConfig {
    
     
        @Value("${redis.maxIdle}")
        private Integer maxIdle;
    
        @Value("${redis.timeout}")
        private Integer timeout;
     
        @Value("${redis.maxTotal}")
        private Integer maxTotal;
     
        @Value("${redis.maxWaitMillis}")
        private Integer maxWaitMillis;
    
    
        @Value("${redis.nodes}")
        private String clusterNodes;
    
    
        /**
         * jedis的正常创建
         * @return
         */
        @Bean
        public JedisCluster getJedisCluster(){
            String[] cNodes = clusterNodes.split(",");
            HashSet<HostAndPort> nodes = new HashSet<>();
            //分割集群节点
            for (String node : cNodes) {
                String[] hp = node.split(":");
                nodes.add(new HostAndPort(hp[0], Integer.parseInt(hp[1])));
            }
                JedisPoolConfig jedisPoolConfig=new JedisPoolConfig();
                jedisPoolConfig.setMaxIdle(maxIdle);
                jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
                jedisPoolConfig.setMaxTotal(maxTotal);
    
            //创建集群对象
            JedisCluster jedisCluster = new JedisCluster(nodes, timeout, jedisPoolConfig);
            return jedisCluster;
        }
    
        /**
         * bloom过滤器的创建
         * @return
         */
        @Bean
        public ClusterClient initClusterClient() {
    
            GenericObjectPoolConfig config = new GenericObjectPoolConfig();
            // 最大连接数
            config.setMaxTotal(300);
            // 池中保留的最大空闲连接数
            config.setMaxIdle(100);
            // 池中保留的最小空闲连接数
            config.setMinIdle(100);
            // 最大等待时间
            config.setMaxWaitMillis(5 * 1000);
            config.setTestWhileIdle(true);
            config.setTestOnBorrow(true);
    
            String[] cNodes = clusterNodes.split(",");
            HashSet<HostAndPort> nodes = new HashSet<>();
            //分割集群节点
            for (String node : cNodes) {
                String[] hp = node.split(":");
                nodes.add(new HostAndPort(hp[0], Integer.parseInt(hp[1])));
            }
    
    
            ClusterClient clusterClient = new ClusterClient(nodes, 300 * 1000, 300 * 1000, 10, config);// 创建REDIS集群
            return clusterClient;
        }
        
    
    }
    

    4、创建BloomJedisService 接口

    public interface BloomJedisService {
    
        public boolean createFilter(final String name, final long initCapacity, final double errorRate);
    
        public boolean[] addMulti(final String name, final byte[]... values);
    
        public boolean[] addMulti(final String name, final String... values);
    
        public boolean add(final String name, final String value);
    
        public boolean add(final String name, final byte[] value);
    
        public boolean exists(final String name, final String value);
    
        public boolean exists(final String name, final byte[] value);
    
        public boolean delete(final String name) ;
    
        public boolean[] existsMulti(final String name, final byte[] value);
    
        public boolean[] existsMulti(final String name, final String... values);
    
        public long expire(final String key, final int seconds);
    
        public long expireAt(final String key, final long unixTime);
    
        public long ttl(final String key) ;
        /**
         * key是否存在
         *
         * @param key
         * @return
         */
        public boolean exists(final String key);
    }
    
    

    5、创建BloomJedisServiceImpl 的具体实现

    @Service
    public class BloomJedisServiceImpl implements BloomJedisService {
    
    
        public static String prefix = "1_BF_";
    
        @Autowired
        private ClusterClient clusterClient;
    
        @Override
        public boolean createFilter(final String name, final long initCapacity, final double errorRate) {
            return clusterClient.createFilter(prefix + name, initCapacity, errorRate);
        }
    
        @Override
        public boolean[] addMulti(final String name, final byte[]... values) {
            return clusterClient.addMulti(prefix + name, values);
        }
        @Override
        public boolean[] addMulti(final String name, final String... values) {
            return clusterClient.addMulti(prefix + name, values);
        }
    
        @Override
        public boolean add(final String name, final String value) {
            return clusterClient.add(prefix + name, value);
        }
    
        @Override
        public boolean add(final String name, final byte[] value) {
            return clusterClient.add(prefix + name, value);
        }
    
        @Override
        public boolean exists(final String name, final String value) {
            return !clusterClient.add(prefix + name, value);
        }
    
        @Override
        public boolean exists(final String name, final byte[] value) {
            return clusterClient.add(prefix + name, value);
        }
    
        @Override
        public boolean delete(final String name) {
            return clusterClient.delete(prefix + name);
        }
    
        @Override
        public boolean[] existsMulti(final String name, final byte[] value) {
            return clusterClient.existsMulti(prefix + name, value);
        }
    
        @Override
        public boolean[] existsMulti(final String name, final String... values) {
            return clusterClient.existsMulti(prefix + name, values);
        }
    
        @Override
        public long expire(final String key, final int seconds) {
            return clusterClient.expire(prefix + key, seconds);
        }
    
        @Override
        public long expireAt(final String key, final long unixTime) {
            return clusterClient.expireAt(prefix + key, unixTime);
        }
    
        @Override
        public long ttl(final String key) {
            return clusterClient.ttl(prefix + key);
        }
    
        /**
         * key是否存在
         *
         * @param key
         * @return
         */
        @Override
        public boolean exists(final String key) {
            return clusterClient.exists(prefix + key);
        }
    
    
    }
    
    

    6、测试功能

        private static final String testFilter = "test_filter2";
    
        @Test
        public void testBloom(){
        //创建过滤器(,这里为了测试比较明显,将概率设置大一点)
            bloomJedisService.createFilter(testFilter, 50, 0.01);
    
            for (int i = 0; i < 1000; i++) {
                bloomJedisService.add(testFilter, ""+i);
            }
            int j=0;
            for (int i = 1001; i < 2000; i++) {
                boolean exists = bloomJedisService.exists(testFilter, "" + i);
                if(exists){
                    System.out.println("有误判"+(++j));
                }
            }
    
        }
    
    

    测试结果 1000/19=0.02 ,基本能接受

    有误判1
    有误判2
    有误判3
    有误判4
    有误判5
    有误判6
    有误判7
    有误判8
    有误判9
    有误判10
    有误判11
    有误判12
    有误判13
    有误判14
    有误判15
    有误判16
    有误判17
    有误判18
    有误判19
    
    展开全文
  • 之前在介绍Redis基本数据结构的那篇文章中,我提到过布隆过滤器,在大数据面前,布隆过滤器有着很棒的性能,也有着很广泛的应用场景,比如垃圾邮件的过滤加粗样式,缓存雪崩、击穿、拦截器等。 什么是布隆过滤器 ...
  • 老早就想在项目中用起来这个优秀的东西。只是成熟的项目又有很多私有客户部署,redis版本可能存在...文章目录概述准备安装部署下载与解压插件编译Redis引入该模块测试Redis是否成功引入模块java客户端测试(redis...
  • 为了实现分布式过滤器,在这里使用了Redis,利用Redis的BitMap实现布隆过滤器的底层映射。 布隆过滤器的一个关键点就是如何根据预计插入量和可接受的错误率推导出合适的BIt数组长度和Hash函数个数,当然Hash函数的...
  • 1.安装redis #下载 [root@localhost redis]# /root/redis [root@localhost redis]# wget https://download.redis.io/releases/redis-5.0.5.tar.gz #解压安装 [root@localhost redis]# tar -zxvf redis-5.0.5.tar.gz...
  • 1、布隆过滤器简介 布隆过滤器是一种数据结构,是一个很长的二进制向量和一系列随机映射函数,可以将其看成一个二进制数组。布隆过滤器可以用于检索一个元素是否在一个集合中。既然布隆过滤器是二进制,那么里面存放...
  • redis布隆过滤器简单使用 布隆过滤器无法直接在redis里使用,需要redis4.0以上的版本才能安装插件使用, 安装方法请我的以前的博客:https://blog.csdn.net/weixin_42564514/article/details/104880452 当然也...
  • java redis 布隆过滤器

    2020-01-13 10:56:34
    redis工具类 import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger...
  • 本篇博客我们主要介绍如何用Redis实现布隆过滤器,但是在介绍布隆过滤器之前,我们首先介绍一下,为啥要使用布隆过滤器。1、布隆过滤器使用场景比如有如下几个需求:①、原本有10亿个号码,现在又来了10万个号码,要...
  • 本篇主要介绍使用redis布隆过滤器插件实现布隆过滤器。Redis 4.0本版之后,增加了redis布隆过滤器的实现插件——rebloom,下面笔者带大家从安装到整合的项目使用一一道来。 1、安装布隆过滤器 在指定目录依次执行...
  • php+redis布隆过滤器

    2020-09-28 15:06:58
    1、布隆过滤器的思想2、布隆过滤器处理流程2.1 开辟空间2.2 寻找hash函数2.3 写入数据2.4 判断3、应用场景4、代码实现(php + redis)4.1 Laravel框架实现的控制器代码4.2 BloomFilter 布隆过滤器(需要依赖redis...
  • 通过Lua脚本批量插入数据到布隆过滤器 有关布隆过滤器的原理之前写过一篇...2、Redis实现布隆过滤器并批量插入数据,并判断当前key值是否存在。 3、针对以上做一个总结。 一、性能对比 主要对以下方法进行性能测试...
  • 1.布隆过滤器使用场景 比如有如下几个需求: ①、原本有10亿个号码,现在又来了10万个号码,要快速准确判断这10万个号码是否在10亿个号码库中? 解决办法一:将10亿个号码存入数据库中,进行数据库查询,准确性有了...
  • 海量数据去重,hash,布隆过滤器、hyperloglog redis如何实现分布式锁延时队列以及限流应用 90分钟搞定redis存储结构原理 LinuxC++后台服务器免费学习地址:C/C++Linux服务器开发/后台架构师-学习视频 问题...
  • Redisson是Redis官方推荐的Java版的Redis客户端,它基于Redis做了更多功能封装,其中就包括布隆过滤器; 结合这三者可以快速的实现一个流式的数据去重功能。 package bill; import org.apache.flink.api.java.tuple...
  • import com.google.common.hash.Funnels; import ... import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.Pipeline; import redis...
  • Redis布隆过滤器安装

    2020-05-19 16:12:46
    RedisBloom 下载地址:https://github.com/RedisBloom/RedisBloom 解压文件:unzip RedisBloom-master.zip 进入目录:cd RedisBloom-master 执行命令:make 目录下生成文件:redisbloom.so 拷贝:cp redis...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 15,052
精华内容 6,020
关键字:

redis布隆过滤器

redis 订阅
友情链接: UART-11111.zip