redis中布隆过滤器使用详解

news2024/11/29 5:45:04

文章目录

  • 一、布隆过滤器介绍
    • 1、什么是布隆过滤器
    • 2、布隆过滤器实现原理
    • 3、误判率
    • 4、布隆过滤器使用场景
    • 5、哈希表与布隆过滤器比较
  • 二、redis中布隆过滤器实战
    • 1.引入redisson依赖
    • 2.创建订单表
    • 3.配置redis
    • 4.配置BloomFilter
    • 5.创建订单
    • 6.单元测试
  • 总结


一、布隆过滤器介绍

1、什么是布隆过滤器

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

通常我们会遇到很多要判断一个元素是否在某个集合中的业务场景,一般想到的是将集合中所有元素保存起来,然后通过比较确定。链表、树、散列表(又叫哈希表,Hash table)等等数据结构都是这种思路。但是随着集合中元素的增加,我们需要的存储空间也会呈现线性增长,最终达到瓶颈。同时检索速度也越来越慢,上述三种结构的检索时间复杂度分别为O(n),O(logN),O(1)

这个时候,布隆过滤器(Bloom Filter)就应运而生。

2、布隆过滤器实现原理

如果想判断一个元素是不是在一个集合中,一般想到的方法是暂存数据,然后查找判定是否存在集合中。这种方法在数据量比较小的情况下适用,但是几个中元素较多的时候,检索速度就会越来越慢。

可以利用Bitmap:只要检查对应点是不是1就可以知道集合中有没有这个数。Bloom filter可以看做是对bitmap的扩展,只是使用多个hash映射函数,从而减低hash发生冲突的概率。

算法如下:
在这里插入图片描述

BloomFilter 是由一个固定大小的二进制向量或者位图(bitmap)和一系列映射函数组成的。

  1. 在初始状态时,对于长度为 m 的位数组,它的所有位都被置为0;
  2. 当有变量被加入集合时,通过 K 个映射函数将这个变量映射成位图中的 K 个点,把它们置为 1;
  3. 查询某个变量是否存在的时候我们只要看看这些点是不是都是 1 就可以大概率知道集合中有没有它了。
  • 如果这些点有任何一个 0,则被查询变量一定不在;
  • 如果都是 1,则被查询变量很可能存在
    为什么说是可能存在,而不是一定存在呢?那是因为映射函数本身就是散列函数,散列函数是会有碰撞的。

3、误判率

这里的误判率是指,BloomFilter 判断某个 key 存在,但它实际不存在的概率

布隆过滤器的误判是由于多个输入经过哈希之后在相同的bit位 置1了,这样就无法判断究竟是哪个输入产生的,因此误判的根源在于相同的 bit 位被多次映射且置 1。

散列函数本身就是会出现碰撞,尽管布隆过滤器中会采用多重hash计算降低冲突概率,但还是无法完全避免,这样就导致一个对象经过多重hash计算的bit位可能和其他对象的bit位重合,如果一个新对象的bit位都被存入其他对象时置为1,那么就会出现误判。

这种情况也造成了布隆过滤器的删除问题,因为布隆过滤器的每一个 bit 并不是独占的,很有可能多个元素共享了某一位。如果我们直接删除这一位的话,会影响其他的元素。

布隆过滤器误判图解:
1、初始状态:布隆过滤器中的bit数组都为0;
2、向布隆过滤器中添加对象X和对象Y,分别经过多重hash计算后,将bit数组中的1,2,4,5,7位置填充为1;
3、当用布隆过滤器判断对象Z是否存在时,先对对象Z进行多重hash计算,对应bit位为4,5,7,而4,5,7刚好已经被对象X和对象Y填充为1,这时就会误判对象Z已经存在。
4、极限情况下当bit数组中的bit位全部被置为1,那么布隆过滤器将会失效。
在这里插入图片描述

特性:

  • 一个元素如果判断结果为存在的时候元素不一定存在,但是判断结果为不存在的时候则一定不存在。
  • 布隆过滤器可以添加元素,但是不能删除元素,因为删掉元素会导致误判率增加。

怎么降低误判率?

  1. 增加散列函数个数,减少bit位冲突的可能;
  2. 增加Bitmap的大小,避免bit位大量覆盖填充。

4、布隆过滤器使用场景

布隆过滤器的典型应用有:

  • 数据库防止穿库, Google Bigtable,HBase 和 Cassandra 以及 Postgresql 使用BloomFilter来减少不存在的行或列的磁盘查找。避免代价高昂的磁盘查找会大大提高数据库查询操作的性能。
  • 业务场景中判断用户是否阅读过某视频或文章,比如抖音或头条,当然会导致一定的误判,但不会让用户看到重复的内容。
  • 缓存宕机、缓存击穿场景,一般判断用户是否在缓存中,如果在则直接返回结果,不在则查询db,如果来一波冷数据,会导致缓存大量击穿,造成雪崩效应,这时候可以用布隆过滤器当缓存的索引,只有在布隆过滤器中,才去查询缓存,如果没查询到,则穿透到db。如果不在布隆器中,则直接返回。
  • WEB拦截器,如果相同请求则拦截,防止重复被攻击。用户第一次请求,将请求参数放入布隆过滤器中,当第二次请求时,先判断请求参数是否被布隆过滤器命中。可以提高缓存命中率。
  • Squid 网页代理缓存服务器在 cache digests 中就使用了布隆过滤器。Google Chrome浏览器使用了布隆过滤器加速安全浏览服务

总的来说,布隆过滤器是用于大数据场景下的重复判断,并且允许有一定误差存在,最典型的使用是解决缓存穿透问题。

5、哈希表与布隆过滤器比较

哈希表也能用于判断元素是否在集合中,但是Bloom Filter只需要哈希表的1/8或1/4的空间复杂度就能完成同样的问题。
哈希表是将真实的元素存放到集合中,而Bloom Filter只是根据元素的多重hash计算结果填充二进制数组,并不存放真正的对象。

Bloom Filter可以插入元素,但是不可以删除已有元素。集合中的元素越多,误报率越大,但是不会漏报。

二、redis中布隆过滤器实战

“纸上谈来终觉浅,绝知此事要躬行”,接下来让我们实战下如何通过布隆过滤器避免订单信息查询的缓存穿透问题。
在这里插入图片描述

1.引入redisson依赖

  <dependency>
      <groupId>org.redisson</groupId>
      <artifactId>redisson-spring-boot-starter</artifactId>
      <version>3.16.7</version>
  </dependency>

2.创建订单表

CREATE TABLE `tb_order` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单Id',
  `order_desc` varchar(50) NOT NULL COMMENT '订单描述',
  `user_id` bigint NOT NULL COMMENT '用户Id',
  `product_id` bigint NOT NULL COMMENT '商品Id',
  `product_num` int NOT NULL COMMENT '商品数量',
  `total_account` decimal(10,2) NOT NULL COMMENT '订单金额',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`),
  KEY `ik_user_id` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=51 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

3.配置redis

新增redis连接属性:

spring.redis.host=192.168.206.129
spring.redis.port=6379
spring.redis.password=123456

配置redisTemplate,主要是设置序列化策略Jackson

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedissonConnectionFactory redisConnectionFactory) {
        //设置序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //反序列化,该设置不能省略,不然从redis获取json转为实体时会报错
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.WRAPPER_ARRAY);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        //配置redisTemplate
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        RedisSerializer stringSerializer = new StringRedisSerializer();
        //key序列化
        redisTemplate.setKeySerializer(stringSerializer);
        //value序列化
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        return redisTemplate;
    }

}

4.配置BloomFilter

/**
 * 配置布隆过滤器
 */
@Configuration
public class BloomFilterConfig {
    @Autowired
    private RedissonClient redissonClient;
    /**
     * 创建订单号布隆过滤器
     * @return
     */
    @Bean
    public RBloomFilter<Long> orderBloomFilter() {
        //过滤器名称
        String filterName = "orderBloomFilter";
        // 预期插入数量
        long expectedInsertions = 10000L;
        // 错误比率
        double falseProbability = 0.01;
        RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter(filterName);
        bloomFilter.tryInit(expectedInsertions, falseProbability);
        return bloomFilter;
    }
}

5.创建订单

redisson中的BloomFilter有2个核心方法:

  • bloomFilter.add(orderId) 向布隆过滤器中添加id
  • bloomFilter.contains(orderId) 判断id是否存在
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {

    @Resource
    private RBloomFilter<Long> orderBloomFilter;

    @Resource
    private TbOrderMapper  tbOrderMapper;

    @Resource
    private RedisTemplate<String,Object> redisTemplate;


    @Override
    public void createOrder(TbOrder tbOrder) {
        //1、创建订单
        tbOrderMapper.insert(tbOrder);

        //2、订单id保存到布隆过滤器
        log.info("布隆过滤器中添加订单号:{}",tbOrder.getId());
        orderBloomFilter.add(tbOrder.getId());
    }

    @Override
    public TbOrder get(Long orderId) {
        TbOrder tbOrder = null;
        //1、根据布隆过滤器判断订单号是否存在
        if(orderBloomFilter.contains(orderId)){
            log.info("布隆过滤器判断订单号{}存在",orderId);
            String key = "order:"+orderId;
            //2、先查询缓存
            Object object = redisTemplate.opsForValue().get(key);
            if(object != null){
                log.info("命中缓存");
                tbOrder =  (TbOrder)object;
            }else{
                //3、缓存不存在则查询数据库
                log.info("未命中缓存,查询数据库");
                tbOrder = tbOrderMapper.selectById(orderId);
                redisTemplate.opsForValue().set(key,tbOrder);
            }
        }else{
            log.info("判定订单号{}不存在,不进行查询",orderId);
        }
        return tbOrder;
    }
}

6.单元测试

    @Test
    public void testCreateOrder() {
        for (int i = 0; i < 50; i++) {
            TbOrder tbOrder = new TbOrder();
            tbOrder.setOrderDesc("测试订单"+(i+1));
            tbOrder.setUserId(1958L);
            tbOrder.setProductId(102589L);
            tbOrder.setProductNum(5);
            tbOrder.setTotalAccount(new BigDecimal("300"));
            tbOrder.setCreateTime(new Date());
            orderService.createOrder(tbOrder);
        }
    }

    @Test
    public void testGetOrder() {
        TbOrder  tbOrder = orderService.get(25L);
        log.info("查询结果:{}", tbOrder.toString());
    }

总结

布隆过滤器的原理其实非常简单,就是bitmap + 多重hash,主要优势就是利用非常小的空间就可以实现在大规模数据下快速判断某一对象是否存在,缺点是存在误判的可能,但不会漏判,也就是存在的对象一定会判断为存在,而不存在的对象会有较低的概率为误判为存在,且不支持对象的删除,因为会增加误判的概率。最典型的使用是解决缓存穿透的问题。

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

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

相关文章

什么是汽车以太网?

总目录链接>> AutoSAR入门和实战系列总目录 总目录链接>> AutoSAR BSW高阶配置系列总目录 文章目录什么是汽车以太网&#xff1f;汽车以太网市场中使用的标准和剖析汽车以太网类型什么是汽车以太网&#xff1f; 本页介绍了汽车以太网的基本特性并提到了汽车以…

【数据库】关系数据库

1.选择关系&#xff08;对行操作&#xff09; 2.投影&#xff08;对列操作&#xff09; &#xff08;行记录重复的不再显示&#xff09; 3.连接&#xff08;从两个关系的笛卡尔积中选出属性间满足一定条件的元组&#xff09; a.等值连接 b.自然连接&#xff08;等值连接的特殊…

【云原生Docker】08-Docker存储

【云原生|Docker】08-Docker存储 文章目录【云原生|Docker】08-Docker存储简介Docker存储挂载方式挂载方式介绍挂载主机目录数据卷容器特性Docker存储示例挂载主机目录Type: bindType: volume总结数据卷容器利用数据卷容器迁移数据备份数据&#xff1a;恢复数据&#xff1a;Doc…

【小程序】小程序组件-1

一. form组件的使用 这个点自己写的时候卡了好久&#xff0c;比较有感悟。 首先明确一点&#xff0c;为什么需要form。 form可以封装一个模块&#xff0c;这个模块里可以有多个input操作&#xff0c;多个输出接口&#xff0c;多个按键&#xff0c;但是至多有两个关键按键&am…

“QT快速上手指南”之计算器(一)Qt Creator,窗口组件

文章目录前言一、什么是QT&#xff1f;二、准备工作&#xff1a;1. 安装Qt Creator&#xff1a;2. 安装Qt SDK&#xff1a;3. 下载安装器&#xff1a;三、窗口组件&#xff1a;四、QT 基本组件的简单介绍&#xff1a;1. QWidget2. QPushButton3. QLabel4. QLineEdit5. QSpinBox…

智能火焰与烟雾检测系统(Python+YOLOv5深度学习模型+清新界面)

摘要&#xff1a;智能火焰与烟雾检测系统用于智能日常火灾检测报警&#xff0c;利用摄像头画面实时识别火焰与烟雾&#xff0c;另外支持图片、视频火焰检测并进行结果可视化。本文详细介绍基于智能火焰与烟雾检测系统&#xff0c;在介绍算法原理的同时&#xff0c;给出Python的…

FPGA基于XDMA实现PCIE X4视频采集HDMI输出 提供工程源码和QT上位机程序和技术支持

目录1、前言2、我已有的PCIE方案3、PCIE理论4、总体设计思路和方案5、vivado工程详解6、驱动安装7、QT上位机软件8、上板调试验证9、福利&#xff1a;工程代码的获取1、前言 PCIE&#xff08;PCI Express&#xff09;采用了目前业内流行的点对点串行连接&#xff0c;比起 PCI …

PE文件解析

PE结构图 工具101editor 文件实列 0-30h为DOS header 40-F0h为DOS STUB 100-1F0h为PE_HEADER 200-2B0h为SECTION_HEADER **虚拟地址&#xff1a;**文件加载到内存中&#xff0c;每个进程都有自己的4GB&#xff0c;这个4GB当中的某个位置叫做虚拟地址 基地址&#xff1a;文件…

23种设计模式之工厂方法模式(黑马程序员)

工厂方法模式一、概述二、结构三、实现四、总结在最后一、概述 定义一个用户创建对象的工厂(接口)&#xff0c;让接口的子类决定去实例化哪个对象&#xff0c;依赖其抽象编程。即工厂方法使一个类的实例化延迟到其工厂的子类。 二、结构 工厂方法模式的主要角色 抽象工厂&a…

计算机组成原理实验二 存储系统预习报告

实验一----静态RAM 一、实验目的 掌握静态随机存储器 RAM 工作特性及数据的读写方法。基于信号时序图&#xff0c;了解读写静态随机存储器的原理。 二、实验预习 1、阅读实验指导书&#xff0c;然后回答问题。 实验所用的静态存储器由一片 6116&#xff08;2K*8bit &#x…

ROS开发之如何在同一个节点订阅、处理、发布消息?

文章目录0、引言1、创建中间特殊文件&#xff08;含订阅者和发布者&#xff09;2、在CMakeLists.txt添加编译规则3、在launch添加启动项4、编译运行5、三维显示0、引言 在ROS应用一般会用到发布者和订阅者&#xff0c;若只接收传感器数据&#xff0c;则只实现订阅者就行&#x…

客户服务 KPI是什么

当企业着手改进其客户服务计划时&#xff0c;必须以可衡量的方式进行。因为如果我们为了改进而改进&#xff0c;没有衡量&#xff0c;我们就永远无法真正知道我们做得有多好&#xff01;如果您的公司已准备好升级其客户服务计划&#xff0c;这里有 12种方法可以衡量和跟踪您的客…

大学生问AI

大学生问AI写在最前面2&#xff0c;描述你在学习工作中碰到的最高级的 AI 是什么&#xff1f;1&#xff0c;你人生中第一次接触到 “人工智能” 的概念和产品是什么&#xff1f; 让你觉得 “人类做得东西的确有智能”&#xff1f;3&#xff0c;你听说过最近的 GPT&#xff0c;n…

Qt5.12實戰之Linux靜態庫與動態庫多文件生成a與so文件並調用

1.編輯並輸入內容到test.cpp與test2.cpp test.cpp #include <stdio.h> int func() {return 888; } test2.cpp #include <stdio.h> int func2() {return 999; } 將test.cpp與test2.cpp編譯成目標文件&#xff1a; g -c test.cpp test2.cpp 一次性生成目標文件…

阅读提升内在美

最近&#xff0c;想在内在美上提升自己&#xff0c;想把玩游戏的时间腾给阅读。不想只是善良&#xff0c;更希望自己拥有智慧拥有力量&#xff0c;更自信更热爱生活。 本篇博文会日日更新&#xff0c;也当作鼓励我继续阅读坚持阅读的动力和监督。 Z-Library 图书馆 分享一个…

LeetCode 1041. 困于环中的机器人

原题链接&#xff1a;1041. 困于环中的机器人 在无限的平面上&#xff0c;机器人最初位于 (0, 0) 处&#xff0c;面朝北方。注意: 北方向 是y轴的正方向。南方向 是y轴的负方向。东方向 是x轴的正方向。西方向 是x轴的负方向。 机器人可以接受下列三条指令之一&#xff1a; …

测试专家须精通Locust

目前随着AI人工智能越来越火&#xff0c;Python 编写的程序越来越多&#xff0c;更多的协议&#xff0c;更多的复杂应用。 所以Locust 是高级性能测试工程师和测试专家&#xff0c;必备技术之一&#xff0c;因为你不可能避免公司中使用python.编写接口或者程序。 在互联网公司…

C#|调用C/C++动态库

参考&#xff1a;C#总结&#xff08;四&#xff09;调用C动态库&#xff08;https://www.shuzhiduo.com/A/A2dmV49qze/&#xff09; 文章目录C#加载C动态库C#加载C#动态库涉及到的概念知识&#xff1a;托管DLL和非托管DLL的区别&#xff08;https://www.tinymind.net.cn/articl…

利用 ELK 处理 Percona 审计日志

Percona Server为 MySQL 数据库服务器进行了改进&#xff0c;在功能和性能上较 MySQL 有着很显著的提升。该版本提升了在高负载情况下的 InnoDB 的性能、为 DBA 提供一些非常有用的性能诊断工具&#xff1b;另外有更多的参数和命令来控制服务器行为 前提 1、有强烈的审计需求。…

网站建设常用的cms建站系统推荐

国内网站建设市场参差不齐&#xff0c;建站公司多如牛毛&#xff0c;网站价格便宜的几百&#xff0c;贵的几十万&#xff0c;作为外行&#xff0c;很难去选择&#xff0c;国内大部分网站建设公司都是营销公司&#xff0c;完全没有底层技术框架的开发能力&#xff0c;90%以上的网…