布隆过滤器详解

news2024/11/16 13:57:18

介绍

本文全部代码地址

布隆过滤器是一种高效的数据结构,用于判断一个元素是否存在于一个集合中.它的主要优点是速度快,空间占用少,因此在需要快速判断某个元素是否在集合中的场合得到广泛引用.

布隆过滤器就是一个大型的位数组和几个不一样的无偏hash函数.所谓无偏就是能够把元素的hash值算的比较均匀.当布隆过滤器说某个值存在时,这个值可能不存在;当它说某个值不存在时,那就肯定不存在.

向布隆过滤器中添加key时,会使用多个hash函数对key进行hash算得一个整数索引值然后对应位数数组长度进行取模运算得到一个位置,每个hash函数都会算得一个不同的位置.再把位数组的这几个位置都置为1就完成了add操作.

向布隆过滤器询问key是否存在时,跟add一样,也会把hash的几个位置都算出来,看看数组中这几个位置是否都为1,只要有一个位为0,那么就说明布隆过滤器中这个key不存在.如果都是1,这并不能说明这个key就一定存在,只是极有可能存在,因为这些位置被置为1可能是因为其他的key存在所致.如果这个位数组长度比较大,存在概率就会很大,如果这个位数组长度比较小,存在的概率就会降低.
在这里插入图片描述

这种方法适用于数据命中不高、数据相对固定、实时性低(通常是数据集较大) 的应用场景,代码维护较为复杂,但是缓存空间占用很少.

实现

初始化数据

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
    `id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
    `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
    `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
    PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `user` VALUES ('be079b29ddc111eda9b20242ac110003', '张三', '北京市海淀区xx街道123号');
INSERT INTO `user` VALUES ('be079b53ddc111eda9b20242ac110003', '李四', '上海市徐汇区xx路456号');
INSERT INTO `user` VALUES ('be079b95ddc111eda9b20242ac110003', '王五', '广州市天河区xx街道789号');
INSERT INTO `user` VALUES ('be079ba4ddc111eda9b20242ac110003', '赵六', '深圳市南山区xx路321号');
INSERT INTO `user` VALUES ('be079bb8ddc111eda9b20242ac110003', '周七', '成都市高新区xx街道654号');
INSERT INTO `user` VALUES ('be079bc5ddc111eda9b20242ac110003', '黄八', '武汉市江汉区xx街道234号');
INSERT INTO `user` VALUES ('be079bd4ddc111eda9b20242ac110003', '罗九', '南京市秦淮区xx路567号');
INSERT INTO `user` VALUES ('be079be2ddc111eda9b20242ac110003', '钱十', '重庆市渝北区xx街道890号');
INSERT INTO `user` VALUES ('be079befddc111eda9b20242ac110003', '周十一', '长沙市岳麓区xx路432号');
INSERT INTO `user` VALUES ('be079bfbddc111eda9b20242ac110003', '吴十二', '西安市雁塔区xx街道765号');

代码实现

这里只展示关于布隆过滤器的核心代码

public class BloomFilterHelper<T> {

    private int numHashFunctions;

    private int bitSize;

    private Funnel<T> funnel;

    public BloomFilterHelper(Funnel<T> funnel, int expectedInsertions, double fpp) {
        Preconditions.checkArgument(funnel != null, "funnel不能为空");
        this.funnel = funnel;
        // 计算bit数组长度
        bitSize = optimalNumOfBits(expectedInsertions, fpp);
        // 计算hash方法执行次数
        numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, bitSize);
    }

    public int[] murmurHashOffset(T value) {
        int[] offset = new int[numHashFunctions];

        long hash64 = Hashing.murmur3_128().hashObject(value, funnel).asLong();
        int hash1 = (int) hash64;
        int hash2 = (int) (hash64 >>> 32);
        for (int i = 1; i <= numHashFunctions; i++) {
            int nextHash = hash1 + i * hash2;
            if (nextHash < 0) {
                nextHash = ~nextHash;
            }
            offset[i - 1] = nextHash % bitSize;
        }

        return offset;
    }

    /**
     * 计算bit数组长度
     */
    private int optimalNumOfBits(long n, double p) {
        if (p == 0) {
            // 设定最小期望长度
            p = Double.MIN_VALUE;
        }
        return (int) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));
    }

    /**
     * 计算hash方法执行次数
     */
    private int optimalNumOfHashFunctions(long n, long m) {
        return Math.max(1, (int) Math.round((double) m / n * Math.log(2)));
    }
}
@Slf4j
@Configuration
public class BloomFilterConfig implements InitializingBean {


    @Autowired
    private StringRedisTemplate template;

    @Autowired
    private UserService userService;

    public static final String BLOOM_REDIS_PREFIX = "bloom_user";

    @Bean
    public BloomFilterHelper<String> initBloomFilterHelper() {
        return new BloomFilterHelper<>((Funnel<String>) (from, into) -> into.putString(from, Charsets.UTF_8)
                .putString(from, Charsets.UTF_8), 1000000, 0.01);
    }

    /**
     * 布隆过滤器bean注入
     *
     * @return
     */
    @Bean
    public BloomRedisService bloomRedisService() {
        BloomRedisService bloomRedisService = new BloomRedisService();
        bloomRedisService.setBloomFilterHelper(initBloomFilterHelper());
        bloomRedisService.setRedisTemplate(template);
        return bloomRedisService;
    }

    /**
     * 初始化方法,将数据库中的id加入到布隆过滤器
     * 也可以不必实现{@link InitializingBean}使用{@link javax.annotation.PostConstruct}注解
     *
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        List<String> idList = userService.getAllUserId();
        log.info("加载用户id到布隆过滤器当中,size:{}", idList.size());
        if (!CollectionUtils.isEmpty(idList)) {
            idList.forEach(item -> {
                bloomRedisService().addByBloomFilter(BLOOM_REDIS_PREFIX, item);
            });
        }
    }
}
public class BloomRedisService {

    private StringRedisTemplate redisTemplate;

    private BloomFilterHelper bloomFilterHelper;

    public void setBloomFilterHelper(BloomFilterHelper bloomFilterHelper) {
        this.bloomFilterHelper = bloomFilterHelper;
    }

    public void setRedisTemplate(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 根据给定的布隆过滤器添加值
     */
    public <T> void addByBloomFilter(String key, T value) {
        Preconditions.checkArgument(bloomFilterHelper != null, "bloomFilterHelper不能为空");
        int[] offset = bloomFilterHelper.murmurHashOffset(value);
        for (int i : offset) {
            redisTemplate.opsForValue().setBit(key, i, true);
        }
    }

    /**
     * 根据给定的布隆过滤器判断值是否存在
     */
    public <T> boolean includeByBloomFilter(String key, T value) {
        Preconditions.checkArgument(bloomFilterHelper != null, "bloomFilterHelper不能为空");
        int[] offset = bloomFilterHelper.murmurHashOffset(value);
        for (int i : offset) {
            if (!redisTemplate.opsForValue().getBit(key, i)) {
                return false;
            }
        }
        return true;
    }
}
@Configuration
public class InterceptorConfiguration implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //注册拦截器
        registry.addInterceptor(authInterceptorHandler())
                .addPathPatterns("/user/get/{id}");
    }

    @Bean
    public BloomFilterInterceptor authInterceptorHandler(){
        return new BloomFilterInterceptor();
    }
}
@Slf4j
public class BloomFilterInterceptor implements HandlerInterceptor {

    @Autowired
    private BloomRedisService bloomRedisService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String currentUrl = request.getRequestURI();
        PathMatcher matcher = new AntPathMatcher();
        //解析出pathvariable
        Map<String, String> pathVariable = matcher.extractUriTemplateVariables("/user/get/{id}", currentUrl);
        //布隆过滤器存储在redis中
        String id = pathVariable.get("id");
        if (bloomRedisService.includeByBloomFilter(BloomFilterConfig.BLOOM_REDIS_PREFIX, id)) {
            log.info("{}极有可能存在,继续向下执行;", id);
            return true;
        }
        /*
         * 不在本地布隆过滤器当中,直接返回验证失败
         * 设置响应头
         */
        log.info("{}不存在,直接返回失败;", id);
        response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
        response.setStatus(HttpStatus.NOT_FOUND.value());
        Result res = new Result(HttpStatus.NOT_FOUND.value(), "用户不存在!", null);
        String result = new ObjectMapper().writeValueAsString(res);
        response.getWriter().print(result);
        return false;
    }
}

测试

存在的数据

在这里插入图片描述

在这里插入图片描述

不存在的数据

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/445954.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

boot-admin整合flowable官方editor-app源码进行BPMN2-0建模(续)

书接上回 项目源码仓库github 项目源码仓库gitee boot-admin 是一款采用前后端分离模式、基于SpringCloud微服务架构的SaaS后台管理框架。系统内置基础管理、权限管理、运行管理、定义管理、代码生成器和办公管理6个功能模块&#xff0c;集成分布式事务Seata、工作流引擎Flow…

ARM学习

计算机硬件基础* 文章目录 知识体系嵌入式系统分层应用开发和底层开发Linux内核五大功能ARM体系结构和接口技术底层知识的学习方法计算机基础知识 计算机的进制计算机的组成总线三级存储结构地址空间CPU原理概述 简述为什么地址总线为32bit的处理器的地址空间为4G简述CPU执行…

Java应用的优雅停机

一. 优雅停机的概念 优雅停机一直是一个非常严谨的话题&#xff0c;但由于其仅仅存在于重启、下线这样的部署阶段&#xff0c;导致很多人忽视了它的重要性&#xff0c;但没有它&#xff0c;你永远不能得到一个完整的应用生命周期&#xff0c;永远会对系统的健壮性持怀疑态度。…

面试被经常问的SQL窗口函数

面试题 有一张“学生成绩表”&#xff0c;包含4个字段&#xff1a;班级id、学生id、课程id、成绩。 问题1&#xff1a; 求出每个学生成绩最高的三条记录 问题2&#xff1a; 找出每门课程都高于班级课程平均分的学生 技术提升 技术要学会分享、交流&#xff0c;不建议闭门造…

图像去模糊:MSSNet 模型详解

本内容主要介绍实现单图像去模糊的 MSSNet 模型。 论文&#xff1a;MSSNet: Multi-Scale-Stage Network for Single Image Deblurring 代码&#xff08;官方&#xff09;&#xff1a;https://github.com/kky7/MSSNet 1. 背景 单图像去模糊旨在从模糊图像&#xff08;由相机抖…

ZLMediaKit在Windows上实现Rtmp流媒体服务器以及模拟rtmp推流和http-flv拉流播放

场景 开源流媒体服务器ZLMediaKit在Windows上运行、配置、按需拉流拉取摄像头rtsp视频流)并使用http-flv网页播放&#xff1a; 开源流媒体服务器ZLMediaKit在Windows上运行、配置、按需拉流拉取摄像头rtsp视频流)并使用http-flv网页播放_霸道流氓气质的博客-CSDN博客 上面讲…

OJ刷题 第十二篇

21308 - 特殊的三角形 时间限制 : 1 秒 内存限制 : 128 MB 有这样一种特殊的N阶的三角形&#xff0c;当N等于3和4时&#xff0c;矩阵如下&#xff1a; 请输出当为N时的三角形。 输入 输入有多组数据&#xff0c;每行输入一个正整数N&#xff0c;1<N<100 输出 按照给出…

从管理摆脱烦躁:几步打造优秀的仓库管理系统

伴随着物流行业的迅速发展&#xff0c;仓库管理成为了各个供应链环节的重要组成部分。为了更好地管理仓库&#xff0c;节约时间、提高效率&#xff0c;越来越多的企业开始使用仓库管理系统。那么&#xff0c;仓库管理系统是什么&#xff0c;为什么要用它呢&#xff1f;本篇文章…

春秋云境:CVE-2022-25578(文件包含)

目录 一、题目 二、蚁剑连接 一、题目 介绍&#xff1a; taocms v3.0.2允许攻击者通过编辑.htaccess文件执行任意代码 进入题目 访问URL&#xff0c;通过弱口令登录后台&#xff1a;admin/tao 进入后台 点击文件管理&#xff1a; 添加AddType application/x-httpd-php .jpg…

【技术】《Netty》从零开始学netty源码(四十一)之PoolChunk

PoolChunk 我们再回顾以下netty中与内存相关的类&#xff1a; 前面我们已经分析了PoolSubpag&#xff0c;本章我们分析PoolChunk,先看下它的属性值&#xff1a; 为了更好的理解这些属性值&#xff0c;我们结合它的构造函数来理解&#xff0c;具体的源码如下&#xff1a; 其…

网络通信之传输层协议

文章目录 传输层在网络通信中扮演的角色认识TCP协议TCP协议的多种机制确认应答(ACK)机制超时重传机制连接管理机制&#x1f53a;滑动窗口流量控制拥塞控制延迟应答捎带应答面向字节流粘包问题TCP异常处理 总结 传输层在网络通信中扮演的角色 上图是网络通信中五个模块&#xff…

信号完整性分析:关于传输线的三十个问题解答(三)

21.FR4 中 50 欧姆传输线的单位长度电感是多少&#xff1f;如果阻抗加倍怎么办&#xff1f;&#xff08;What is the inductance per length of a 50-Ohm transmission line in FR4? What if the impedance doubles?&#xff09; FR4 中的所有 50 欧姆传输线的单位长度电感约…

Vector - 常见设备I/O设置及使用

做过协议测试的朋友基本都会知道vector相关的设备&#xff0c;而且很多人都会去使用IO接口&#xff0c;但是并非所有的I/O接口都是一样的&#xff0c;然而他们到底是什么样的呢&#xff1f;这个需要我们去识别不同的设备&#xff0c;然后根据不同的设备去使用对应的IO配置&…

基于docker安装mongo

1.背景 基于业务的需求,部分数据量较大,基本一天的数据量都在1亿左右,关系型数据库已经满足不了了要求,所以更换为非关系型数据库.当然非关系型数据库的种类有很多,我们选择的是mongo,要想了解熟悉mongo,不能只做书面上的功夫,必须较强实际操作,才可以得心应手. 2. 安装mongo…

软件测试概念篇(上)

作者&#xff1a;爱塔居 专栏&#xff1a;软件测试​​​​​ 作者简介&#xff1a;大三学生&#xff0c;希望和大家一起好好学习&#xff01; 文章简介&#xff1a;简述了软件测试方面的基础概念 文章目录 目录 文章目录 一、软件测试概念 二、调试和测试的区别 三、软件测试和…

【linux】对于权限的理解

权限 Linux权限的概念用户之间的切换 Linux权限管理文件权限操作文件的人Linux文件默认权限的设置权限掩码 所属组/其他删除拥有者创建的文件文件拥有者、所属组的修改修改文件拥有者修改文件所属组一次性修改拥有者和所属组 目录的执行权限 Linux权限的概念 首先&#xff0c;…

ROS主机搭建NFS服务器,虚拟机通过挂载访问及修改主机文件

本文主要介绍在ROS主机中搭建NFS服务器&#xff0c;虚拟机&#xff08;从机&#xff09;通过nfs挂载的方式访问及修改主机中文件的方法 一、ROS主机NFS服务器搭建&#xff1a; 若机器人配有显示屏&#xff0c;此部分可直接在机器人上操作&#xff0c;否则&#xff0c;可通过SSH…

java 一文讲透API [常用类 + 常用工具](20万字博文)

目录 一、前言 二、API 简介 1.什么是API? 2.java中组件的层次结构 : 3.什么是模块 三、API&#xff08;常用类&#xff09; 1.Object类 2.String类 3.StringBuilder 和 StringBuffer类 4.Math类 和 System类 5.Date 和 Calender类 6.八大包装类 7.常用类补充 —— Arra…

案例分享 | 汽车电机控制箱螺钉浮高检测

电机控制器是通过主动工作来控制电机按照设定的方向、速度、角度、响应时间进行运动的集成电路&#xff0c;日常生活中的洗衣机、冰箱、印刷机等设备都需要电机控制器来控制其运行工作&#xff0c;是各种机械设备中不可或缺的部件。 在电动车辆中&#xff0c;电机控制器也是关…

C++的类和对象(1)

类和对象 1.面向过程和面向对象的初步认识2.类的引入3. 类的定义 &#xff08;class关键字&#xff09;4. 类的访问限定符及封装4.1. 类的访问限定符4.2. 类的封装什么是封装&#xff1f;为什么要封装&#xff1f; 5.类的作用域6.类的实例化 1.面向过程和面向对象的初步认识 C…