Redis-布隆过滤器

1. 布隆过滤器(Bloom Filter)原理以及应用

假设现在有50亿个电话号码,现在有1万个电话号码,需要快速判断这些电话号码是否已经存在?

现在有3中途径

  • 通过数据库查询,但是不能快速查询。
  • 把电话号码预先放在一个集合中,如果用long类型存储的话,50亿 * 8字节 = 大于需要40GB(内存浪费或者严重不够)
  • 使用redis的hyperloglog,但是准确度不高。

类似的问题:

  • 垃圾邮件过滤
  • 文字处理中的错误单词检测
  • 网络爬虫重复URL检测
  • 会员抽奖
  • 判断一个元素在亿级数据中是否存在
  • 缓存穿透
  • 而布隆过滤器则可以解决上述问题

1 什么是布隆过滤器

布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。

当一个元素被加入集合时,通过 K 个 Hash 函数将这个元素映射成一个位阵列(Bit array)中的 K 个点,把它们置为 1。检索时,我们只要看看这些点是不是都是 1 就(大约)知道集合中有没有它了:
如果这些点有任何一个 0,则被检索元素一定不在; 如果都是 1,则被检索元素很可能在。

添加元素的原理

  1. 将要添加的元素给k个hash函数
  2. 得到对应于位数组上的k个位置
  3. 将这k个位置设置成 1

查询元素原理

  1. 将要查询的元素给k个hash函数
  2. 得到对应数组的k个元素
  3. 如果k个位置中有一个为0,则肯定不在集合中
  4. 如果k个位置全部为1,则有可能在集合中

在这里插入图片描述

优点

它的优点是空间效率和查询时间都远远超过一般的算法,布隆过滤器存储空间和插入 / 查询时间都是常数O(k)。另外, 散列函数相互之间没有关系,方便由硬件并行实现。布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。

缺点

随着数据的增加,误判率随之增加;只能判断数据是否一定不存在,而无法判断数据是否一定存在。

如果数据A,经过hash1(A)、hash2(A)、hash3(A),得到其hash值1、3、5,然后我们在其二进制向量位置1、3、5设置1,然后数据B,经过hash1(B)、hash2(B)、hash3(B),其实hash值也是1、3、5,我们在做业务处理的时候判断B是否存在的时候发现 其二进制向量位置返回1,认为其已经存在,就跳过相关业务处理,实际上根本不存在,这就是由于hash碰撞引起的问题。也就存在了误差率。

无法做到删除数据

一般情况下不能从布隆过滤器中删除元素. 我们很容易想到把位数组变成整数数组,每插入一个元素相应的计数器加 1, 这样删除元素时将计数器减掉就可以了。然而要保证安全地删除元素并非如此简单。首先我们必须保证删除的元素的确在布隆过滤器里面. 这一点单凭这个过滤器是无法保证的。

扫描二维码关注公众号,回复: 12462289 查看本文章

2. redis实现布隆过滤器的方式

1. guava单机版实现布隆过滤器

引入Guava依赖

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>23.0</version>
</dependency>

代码实现

package jedis.bloomFilter;

import com.google.common.hash.Funnels;
import com.google.common.hash.BloomFilter;

import java.util.ArrayList;
import java.util.List;

public class GuavaBloomFilter {
    private static  int size = 10000;
    public static void main(String[] args) {
        /**
         * 默认误差率3%。肯定不存在以及可能存在
         * 可通过构造函数去设置误差率
         *  create(
         *       Funnel<? super T> funnel, int expectedInsertions, double fpp)
         *
         */
        BloomFilter<Integer> bloomFilter =  BloomFilter.create(Funnels.integerFunnel(), size);
        for (int i = 0; i < size; i++) {
            bloomFilter.put(i);
        }

        for (int i = 0; i < size; i++) {
            if (!bloomFilter.mightContain(i)) {
                System.out.println("有人逃脱了");
            }
        }

        List<Integer> list = new ArrayList<Integer>(1000);
        for (int i = size + 10000; i < size + 20000; i++) {
            if (bloomFilter.mightContain(i)) {
                list.add(i);
            }
        }
        System.out.println("误伤的数量:" + list.size());

    }
}

2. Rebloom插件方式实现布隆过滤器(redis 4.0 以后)

下载并编译

$ git clone git://github.com/RedisLabsModules/rebloom
$ cd rebloom
$ make
cd /usr/redis-4.0.11
#加载module 并设置容量与容错率
# 容量100万, 容错率万分之一
./src/redis-server redis.conf --loadmodule /usr/rebloom/rebloom.so INITIAL_SIZE 1000000 ERROR_RATE 0.0001

redis命令:

BF.ADD bloom redis
BF.EXISTS bloom redis
BF.EXISTS bloom nonxist

3. 手动实现

package com.jd.demo.test;

import java.util.Arrays;
import java.util.BitSet;
import java.util.concurrent.atomic.AtomicBoolean;

public class MyBloomFilter {
    //你的布隆过滤器容量
    private static final int DEFAULT_SIZE = 2 << 28;
    //bit数组,用来存放结果
    private static BitSet bitSet = new BitSet(DEFAULT_SIZE);
    //后面hash函数会用到,用来生成不同的hash值,可随意设置,别问我为什么这么多8,图个吉利
    private static final int[] ints = {1, 6, 16, 38, 58, 68};

    //add方法,计算出key的hash值,并将对应下标置为true
    public void add(Object key) {
        Arrays.stream(ints).forEach(i -> bitSet.set(hash(key, i)));
    }

    //判断key是否存在,true不一定说明key存在,但是false一定说明不存在
    public boolean isContain(Object key) {
         boolean result = true;
        for (int i : ints) {
        	//短路与,只要有一个bit位为false,则返回false
            result = result && bitSet.get(hash(key, i));
        }
        return result;
    }

    //hash函数,借鉴了hashmap的扰动算法
    private int hash(Object key, int i) {
        int h;
        return key == null ? 0 : (i * (DEFAULT_SIZE - 1) & ((h = key.hashCode()) ^ (h >>> 16)));
    }
}

一个在线计算所需空间的地址

计算空间

参考文档:
文档1
文档2

猜你喜欢

转载自blog.csdn.net/bj_ameng/article/details/109078150
今日推荐