当前位置: 首页 > article >正文

RedissonClient妙用-分布式布隆过滤器

目录

布隆过滤器介绍

布隆过滤器的落地应用场景

高并发处理 

多个过滤器平滑切换

分析总结


布隆过滤器介绍

布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。

它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。

什么业务场景需要使用这个布隆过滤器呢?我个人觉得是对误判数据不敏感。比如,在一个质检系统中,客服人员对重复的录音是非常敏感的,至于少了一些录音,对他们来说是无所谓的。

刚刚好,我们使用布隆过滤器对录音文件名进行过滤,布隆过滤器返回true的时候,我们把这部分录音给丢弃掉,返回false的时候,这部分数据就入库。而布隆过滤器返回false的时候,说明这个数据是100%不存在的,满足我们的应用场景。

布隆过滤器的落地应用场景

过滤代码

package com.tml.mouseDemo.service;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import java.time.Duration;

/**
 * 分布式布隆过滤器的实现
 */
@Service
@Slf4j
public class BloomFilterService {

    @Autowired
    private RedissonClient redissonClient;

    private RBloomFilter bloomFilter;

    @PostConstruct
    public void init() {
        //参数:布隆过滤器的名字
        bloomFilter = redissonClient.getBloomFilter("repeatAudioFileName");
        // 初始化布隆过滤器  预计数据量   误判率
        boolean b = bloomFilter.tryInit(50000L, 0.03);

        log.info("repeatAudioFileName bloomFilter tryInit :{}", b);
    }


    public boolean checkFileNameRepeat(String audioFileName) {
        if (!StringUtils.hasText(audioFileName)) {
            throw new NullPointerException("audioFileName is empty");
        }

        //通过setNx的原子操作,保证在多个布隆过滤器之间有一个平滑的过度
        boolean setIfAbsent = redissonClient.getBucket(audioFileName).setIfAbsent("1", Duration.ofHours(1));
        if (!setIfAbsent) {
            log.info("this file is repeat!");
            return true;
        }

        boolean contains = bloomFilter.contains(audioFileName);
        if (!contains) {
            boolean add = bloomFilter.add(audioFileName);
            log.info("checkFileNameRepeat not contain:{} add:{}", audioFileName, add);
            //添加失败,说明过滤器中已经存在这个元素了
            return !add;
        }
        return true;
    }

}

代码说明

高并发处理 

contains()和add()是两个操作,在多线程并发条件下,需要结合这两个方法的返回值来综合判断,是不是布隆过滤器包含这个元素。

多个过滤器平滑切换

setIfAbsent()这个操作是一个更加严谨的操作,考虑到实际场景中是有多个布隆过滤器的,在第一个布隆过滤器和第二个布隆过滤器进行切换的时候,怎么做到平滑的切换呢?

比如,我们的应用场景中,每天都会创建一个布隆过滤器,而录音的数据是源源不断的推送过来的,但是我们录音数据有一个特点是,相同的录音的数据可能会多次推送,并且多次的最大间隔不会超过1小时

假设repeatAudioFileName-20240206这个过滤器中已经包含了某个录音文件A,刚刚好时间到了20230207这天,需要重新创建布隆过滤器,在repeatAudioFileName-20240207这个过滤器中,恰好又有相同的文件进来了需要判断,在新的过滤器中刚好没有这个文件,这个时候,又会将录音A文件入库,这个就是业务异常了。

优化后的方案如下

优化的方案的代码就是如上

对应的压测代码也发一下

    @Test
    public void testRedis() throws InterruptedException {


        int threadSize = 100;
        String fileName = "sagfdsfgewfgdsghf25870.mkv";
        long start = System.currentTimeMillis();
        CyclicBarrier cyclicBarrier = new CyclicBarrier(threadSize);
        CountDownLatch countDownLatch = new CountDownLatch(threadSize);
        for (int i = 0; i < threadSize; i++) {
            new Thread(() -> {
                try {
                    cyclicBarrier.await();
                    boolean b = bloomFilterService.checkFileNameRepeat(fileName);
                    log.info("checkFileNameRepeat----------:{}", b);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    countDownLatch.countDown();
                }
            }, "repeat_test_" + i).start();

        }

        countDownLatch.await();

        long end = System.currentTimeMillis();

        log.info("start:{}-- cost:{} ms", start, (end - start));



    }

分析总结

布隆过滤器有对应的优缺点,是不是使用你们的业务场景,需要想清楚。上面的案例中,之所以不用数据库的唯一约束,是因为我们使用了sharding-jdbc分库分表,相同的文件名的数据对应的订单id不一样,也不是在一个表中,不好控制。

顺便说一下,布隆过滤器的应用场景还是很广泛的,在以太坊ETH底层实现中,就用了布隆过滤器。


http://www.kler.cn/a/232459.html

相关文章:

  • Leecode刷题C语言之统计好节点的数目
  • 多窗口切换——selenium
  • 策略模式、状态机详细解读
  • C++ —— 哈希详解 - 开散列与闭散列
  • Iotop使用
  • 高防服务器的费用受到哪些原因影响?
  • 解锁机器学习多类分类之门:Softmax函数的全面指南
  • 详细关于如何解决mfc140.dll丢失的步骤,有效修复mfc140.dll文件丢失的问题。
  • l + r >> 1; 的含义
  • 10分钟快速入门正则表达式
  • 100天精通Python(实用脚本篇)——第115天:基于selenium实现反反爬策略之隐藏浏览器指纹特征
  • 排序(2)(希尔排序)
  • RibbonOpenFeign源码(待完善)
  • C语言操作符超详细总结
  • 第十六篇【传奇开心果系列】Python的OpenCV库技术点案例示例:图像质量评估
  • 前端框架学习 Vue(3)vue生命周期,钩子函数,工程化开发脚手架CLI,组件化开发,组件分类
  • Git分支常用指令
  • LabVIEW多任务实时测控系统
  • 书生·浦语大模型第二课作业
  • 基于Linux的HTTP代理服务器搭建与配置实战
  • Gitlab和Jenkins集成 实现CI (三)
  • (力扣)1314.矩阵区域和
  • 【stomp实战】websocket原理解析与简单使用
  • 机器学习7-K-近邻算法(K-NN)
  • SQL笔记-2024/01/31
  • 前后端通讯:前端调用后端接口的五种方式,优劣势和场景