布隆过滤器原理及应用

使用场景

适用于数据命中不高、 数据相对固定、 实时性低(通常是数据集较大) 的应用场景。比如:

  1. 解决缓存穿透;
  2. 爬虫时记录已爬取的网页;
  3. 记录黑名单;

原理

数据结构是一个bit数组,布隆过滤器通过hash算法(无偏hash函数)将值换算成对应的bit位,并存入布隆过滤器。所谓无偏hash函数就是能够把元素的hash值算的比较均匀。
如果查询的值经过hash换算成值后在布隆过滤器没有找到对应的bit(存在一个bit位不匹配),表示该值一定不存在,如果找到了对应的匹配位,也不能确定该值一定存在,因为有可能是别的值占据了对应的bit(hash冲突)。
可以多次hash生成多个bit位,降低hash冲突概率。

值的存储示意图如下:

布隆过滤器原理及应用_第1张图片

优缺点

优点

缓存空间占用很少,效率高。

缺点

  • 不支持删除

因为多个不同的数据对应的可能是同一组bit位,如果删除了一个数据bit位可能将其他的数据对应bit位也删除了。
可以通过维护一个counter对应bit位来支持删除。

  • 存在一定误差

判断某一个值存在,存在一定误差,因为hash碰撞导致不同值生成的哈希值相同,当某个不存在的值计算的hash值已存在,则得出的结论也是存在的。为了减少误差率,可以增加hash的次数和增加bit数组位的长度;

实现方式

redis

redis中的布隆过滤器,redis4.0之后加入了module,适合分布式场景。

引入redisson客户端:

<dependency>
   <groupId>org.redisson</groupId>
   <artifactId>redisson</artifactId>
   <version>3.6.5</version>
</dependency>

示例伪代码:

public class RedissonBloomFilter {

    public static void main(String[] args) {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://localhost:6379");
        // 构造Redisson
        RedissonClient redisson = Redisson.create(config);

        RBloomFilter<String> bloomFilter = redisson.getBloomFilter("nameList");
        // 初始化布隆过滤器:预计元素为100000000L(1亿),误差率为3%,根据这两个参数会计算出底层的bit数组大小
        bloomFilter.tryInit(100000000L, 0.03);
        // 将jay插入到布隆过滤器中
        bloomFilter.add("jay");

        // 判断下面号码是否在布隆过滤器中
        System.out.println(bloomFilter.contains("jolin")); // false
        System.out.println(bloomFilter.contains("kunlin")); // false
        System.out.println(bloomFilter.contains("jay")); // true
    }
}

使用布隆过滤器需要把所有数据提前放入布隆过滤器,并且在增加数据时也要往布隆过滤器里放,布隆过滤器缓存过滤伪代码:

// 初始化布隆过滤器
RBloomFilter<String> bloomFilter = redisson.getBloomFilter("nameList");
// 初始化布隆过滤器:预计元素为100000000L(1亿),误差率为3%
bloomFilter.tryInit(100000000L,0.03);
        
// 把所有数据存入布隆过滤器
void init(){
    for (String key: keys) {
        bloomFilter.put(key);
    }
}

String get(String key) {
    // 从布隆过滤器这一级缓存判断下key是否存在
    Boolean exist = bloomFilter.contains(key);
    if(!exist){
        return "";
    }
    // 从缓存中获取数据
    String cacheValue = cache.get(key);
    // 缓存为空
    if (StringUtils.isBlank(cacheValue)) {
        // 从存储中获取
        String storageValue = storage.get(key);
        cache.set(key, storageValue);
        // 如果存储数据为空,需要设置一个过期时间(300秒)
        if (storageValue == null) {
            cache.expire(key, 60 * 5);
        }
        return storageValue;
    } else {
        // 缓存非空
        return cacheValue;
    }
}

注意:布隆过滤器不能删除数据,如果要删除得重新初始化数据。

guava

google的guava包实现了布隆过滤器,适合单机场景。

实际应用

抢单王通过布隆过滤器判断电子行业料号是否存在,不存在才继续流程。线上redis版本为2.8,不支持redis提供的布隆过滤器,通过guava来实现的,guava是google提供的java核心类库。

你可能感兴趣的:(数据结构与算法,布隆过滤器,bool,bloomFilter,缓存穿透,redis)