【Redis】Redis中的布隆过滤器

news2025/1/13 10:21:43

【Redis】Redis中的布隆过滤器

前言

在实际开发中,会遇到很多要判断一个元素是否在某个集合中的业务场景,类似于垃圾邮件的识别,恶意IP地址的访问,缓存穿透等情况。类似于缓存穿透这种情况,有许多的解决方法,如:Redis存储Null值等,而对于垃圾邮件的识别,恶意IP地址的访问,我们也可以直接用 HashMap 去存储恶意IP地址以及垃圾邮件,然后每次访问时去检索一下对应集合中是否有相同数据。这种思路对于数据量小的项目来说是没有问题的,但是对于大数据量的项目,如:垃圾邮件出现有几十万,恶意IP地址出现有上百万,那么这些大量的数据就会占据大量的空间,这个时候就可以考虑一下布隆过滤器了。

布隆过滤器是什么?

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

在这里插入图片描述

可以把布隆过滤器理解为一个不怎么精确的 set 结构,当你使用它的 contains方法判断某个对象是否存在时,它可能会误判。但是布隆过滤器也不是特别不精确,只要参数设置得合理,它的精确度也可以控制得相对足够精确,只会有小小的误判概率。

当布隆过滤器说某个值存在时,这个值可能不存在;当它说某个值不存在时那就肯定不存在。打个比方,当它说不认识你时,肯定就是真的不认识;而当它说认识你时,却有可能根本没见过你,只是因为你的脸跟它认识的某人的脸比较相似(某些熟脸的系数组合),所以误判以前认识你。

一句话总结:由一个初始值为零的bit数组和多个哈希函数构成,用来快速判断集合中是否存在某个元素。

使用bit数组的目的就是减少内存的占用,数组不保存数据信息,只是在内存中存储一个是否存在的表示0或1

布隆过滤器的优缺点:

优点:

​ 高效插入和查询,内存占用空间少

缺点:

  • 存在误判,不能精确过滤
  • 不能删除元素

布隆过滤器的使用场景

黑白名单校验、识别垃圾邮件

发现存在黑名单中的,就执行特定操作。比如:识别垃圾邮件,只要是邮箱在黑名单中的邮件,就识别为垃圾邮件。假设黑名单的数量是数以亿计的,存放起来就是非常耗费存储空间的,布隆过滤器则是一个较好的解决方案。把所有黑名单都放在布隆过滤器中,在收到邮件时,判断邮件地址是否在布隆过滤器中即可。

解决缓存穿透的问题

把已存在数据的key存在布隆过滤器中,相当于Redis前面挡着一个布隆过滤器。当有新的请求时,先到布隆过滤器中查询是否存在:如果布隆过滤器中不存在该条数据则直接返回;如果布隆过滤器中已存在,才去查询缓存Redis,如果Redis里没查询到则再查询MySQL数据库

布隆过滤器的原理

每个布隆过滤器对应到 Redis 的数据结构里面就是一个大型的位数组和几个不-样的无偏 hash函数,如下图中的F、G、H就是这样的hash函数。所谓无偏就是能够把元素的 hash 值算得比较均匀,让元素被 hash映射到位数组中的位置比较随机。

在这里插入图片描述

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

向布隆过滤器询问 key 是否存在时,跟add 一样,也会把 hash 的几个位置都算出来,**看看位数组中这几个位置是否都为 1,只要有一个位为 0,那么说明布隆过滤器中这个 key 不存在。如果这几个位置都是 1,并不能说明这个 key 就一定存在,只是极有可能存在,因为这些位被置为 1 可能是因为其他的 key 存在所致。**如果这个位数组比较稀疏,判断正确的概率就会很大,如果这个位数组比较拥挤,判断正确的概率就会降低。具体的概率计算公式比较复杂,感兴趣可以阅读相关的更深入研究的资料,不过非常烧脑,不建议读者细看。

参考博客:Redis系列–布隆过滤器(Bloom Filter)_redistemplate 布隆过滤器_幼儿园里的山大王的博客-CSDN博客

基于Redisson的布隆过滤器使用实例

1.引入Redisson依赖

<!--原生-->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.13.4</version>
</dependency>

<!--或者另一种Spring集成starter-->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.13.6</version>
</dependency>

2.配置Redisson

@Configuration
public class RedissionConfig {
    @Value("${spring.redis.host}")
    private String redisHost;

    @Value("${spring.redis.password}")
    private String password;

    private int port = 6379;

    @Bean
    public RedissonClient getRedisson() {
        Config config = new Config();
        config.useSingleServer().
                setAddress("redis://" + redisHost + ":" + port).
                setPassword(password);
        config.setCodec(new JsonJacksonCodec());
        return Redisson.create(config);
    }
}

3.配置布隆过滤器

@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;
    }
}

4.创建订单表

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;

5.编写业务处理代码

@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/900738.html

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

相关文章

高效的WMS系统手持盘点方案

WMS系统手持盘点就是指利用WMS系统支持的手持式电子盘点设备进行库存盘点的方式。 具体来说: - 手持盘点设备是一种小型的电子设备,具有移动条形码扫描功能,可以实时与WMS系统联通。 - WMS系统利用手持设备,可以给仓储人员下发具体的盘点任务,例如需要盘点的货位、商品等信息…

mybatis plus 配置自动设置创建时间和创建人id

1.新建 MyMetaObjectHandler package com.ruoyi.framework.config;import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import com.ruoyi.common.bean.LocalUser; import com.ruoyi.coupon.domain.CouponUser; import org.apache.ibatis.reflection.MetaObjec…

软件报错msvcr90.dll丢失的解决方法,亲测可以修复

我曾经遇到过一个令人头疼的问题&#xff1a;msvcr90.dll丢失。这个问题导致了我的程序无法正常运行&#xff0c;让我感到非常苦恼。然而&#xff0c;在经过一番努力后&#xff0c;我终于成功地修复了这个问题&#xff0c;这让我感到非常欣慰和满足。 msvcr90.dll丢失的原因可能…

python高级基础

文章目录 python高级基础闭包修饰器单例模式跟工厂模式工厂模式单例模式 多线程多进程创建websocket服务端手写客户端 python高级基础 闭包 简单解释一下闭包就是可以在内部访问外部函数的变量&#xff0c;因为如果声明全局变量&#xff0c;那在后面就有可能会修改 在闭包中的…

深入探讨API接口测试:从基础到高级策略

引言&#xff1a;API测试的重要性 在当前的技术趋势中&#xff0c;API&#xff08;应用程序接口&#xff09;已经成为连接各种系统和服务的基石。API不仅仅是大型企业的领域&#xff0c;中小型公司和初创公司也越来越依赖API来拓展他们的业务功能和跨系统通信。正因如此&#…

企业数字化转型大数据湖一体化平台项目建设方案PPT

导读&#xff1a;原文《企业数字化转型大数据湖一体化平台项目建设方案PPT》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 喜欢文章&#xff0c;您可以点赞评论转发…

(搜索) 剑指 Offer 38. 字符串的排列 ——【Leetcode每日一题】

❓剑指 Offer 38. 字符串的排列 难度&#xff1a;中等 输入一个字符串&#xff0c;打印出该字符串中字符的所有排列。 你可以以任意顺序返回这个字符串数组&#xff0c;但里面 不能有重复元素。 示例: 输入&#xff1a;s “abc” 输出&#xff1a;[“abc”,“acb”,“bac”…

【学习日记】【FreeRTOS】时间片的实现

前言 本文以野火的教程和代码为基础&#xff0c;对 FreeRTOS 中时间片的概念作了解释&#xff0c;并且给出了实现方式&#xff0c;同时发现并解决了野火教程代码中的 bug。 一、时间片是什么 在前面的文章中&#xff0c;我们已经知道任务根据不同的优先级被放入就绪列表中不…

符号随机梯度下降算法SIGNSGD

考虑随机优化问题&#xff1a; 符号随机梯度下降(SIGNSGD)算法&#xff1a; 假设基础&#xff1a; 收敛定理&#xff1a; 联邦优化&#xff1a;

[Java优选系列第2弹]SpringMVC入门教程:从零开始搭建一个Web应用程序

想和你们分享我眼里的代码世界&#x1f5fa;️ 优选系列持续更新中&#x1f4ab; 一直在等你&#xff0c;你终于来啦&#x1f496; 绿色代表解释说明 黄色代表重点 红色代表精髓 SpringMVC是一个基于Java的Web框架&#xff0c;它使用了MVC&…

旅行越野SUV——捷途旅行者即将与大家见面!

从2020年开始,国内车市踊跃出许多性格鲜明的车型,例如坦克300、极氪001、蔚来ET5旅行版以及五菱悦野,虽然它们属于小众车型,但奈何销量都非常亮眼,这也从另一角度证明现如今的年轻消费群体很喜欢特点鲜明的汽车产品。前段时间,捷途汽车发布的捷途旅行者,一款定位旅行越野SUV的新…

JVM的前世今生之类加载过程

1. 什么是JVM VM是JavaVirtualMachine&#xff08;Java虚拟机&#xff09;的缩写&#xff0c;JVM是一种用于计算设备的规范&#xff0c;它是一个虚构出来的计算机&#xff0c;是通过在实际的计算机上仿真模拟各种计算机功能来实现的。它可以实现跨操作系统运行&#xff0c;即一…

CMake语法复习

前言 此文总结了库的制作和一些CMake常用的一些语法。 一&#xff1a;创建静态库和动态库 静态库的生成和使用 动态库的生成和使用 二&#xff1a;使用CMake来生成Makefile&#xff0c;生成可执行文件 顶层目录下的CMakeLists.txt project(HELLO) add_subdirectory(libhell…

设计模式之备忘录模式(Memento)的C++实现

1、备忘录模式的提出 在软件功能开发过程中&#xff0c;某些对象的状态在转换过程中&#xff0c;由于业务场景需要&#xff0c;要求对象能够回溯到对象之前某个点的状态。如果使用一些共有接口来让其他对象得到对象的状态&#xff0c;便会暴露对象的实现细节。备忘录模式是在不…

ps打开出现dll文件丢失怎么回事?该如何修复

今天在看到一个ps的相关问题&#xff0c;可能很多同学也遇到过&#xff0c;安装好ps软件之后&#xff0c;打开会提示无法启动此程序。然后会有各种文件的丢失一般都是xxx.dll文件的格式&#xff0c;并提示尝试重新安装该程序&#xff0c;以解决此问题。不但是PS&#xff0c;还有…

226、仿真-基于51单片机楼道教室走道智能灯光光照人体感应检测控制Proteus仿真设计(程序+Proteus仿真+配套资料等)

毕设帮助、开题指导、技术解答(有偿)见文未 目录 一、硬件设计 二、设计功能 三、Proteus仿真图 四、程序源码 资料包括&#xff1a; 需要完整的资料可以点击下面的名片加下我&#xff0c;找我要资源压缩包的百度网盘下载地址及提取码。 方案选择 单片机的选择 方案一&…

Hlang--用Python写个编程语言-函数与基本数据结构实现

文章目录 前言语法表述解析器修改词法解析函数节点函数节点解析List的解析实现解释器节点函数操作String和List处理总结前言 okey,经过一段时间的努力,接下来要实现的是函数。当然还有对应的基本数据结构,那么之后的话,我们的工作就开始进一步转换了。 那么在这块我们要实…

java:数据库连接池

概念 举个例子来说吧&#xff0c;假设我们开了一家餐馆&#xff0c;客人来了&#xff0c;我们就请一个服务员&#xff0c;使用完后再把他开除了&#xff0c;下个客人再来了&#xff0c;我们再请一个&#xff0c;使用完再开除。 这是不是我们现在使用 JDBC 连接数据库的场景&a…

电脑提示缺少vcomp140.dll怎样修复-vcomp140.dll丢失怎么办?

在使用电脑软件或者玩游戏时候&#xff0c;我们有时候会遇到提示系统丢失DLL状况&#xff0c;相信很多用电脑的都遇到过&#xff0c;我曾多次遇到提示丢失DLL&#xff0c;每次都要折腾了几小时&#xff0c;终于&#xff01;在我多次折腾后&#xff0c;&#xff0c;有幸得到这个…

ForkJoinPool 你真的明白和用对了吗

ForkJoinPool 是一个功能强大的 Java 类&#xff0c;用于处理计算密集型任务&#xff0c;使用 ForkJoinPool 分解计算密集型任务&#xff0c;并并行执行它们&#xff0c;能够产生更好的性能。它的工作原理是将任务分解成更小的子任务&#xff0c;使用分而治之的策略进行操作&am…