【Redis】Redis中的布隆过滤器

【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,主要优势就是利用非常小的空间就可以实现在大规模数据下快速判断某一对象是否存在,缺点是存在误判的可能,但不会漏判,也就是存在的对象一定会判断为存在,而不存在的对象会有较低的概率为误判为存在,且不支持对象的删除,因为会增加误判的概率。最典型的使用是解决缓存穿透的问题。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LDAP: error code 53 - unauthenticated bind (DN with no password) disallowed

这个错误提示显示Jenkins无法连接到LDAP服务器&#xff0c;原因是LDAP服务器不允许未认证的绑定&#xff08;DN与无密码&#xff09;。 但实际填写了DN

UITableView自定义TableHeader和TableFooter

UITableView自定义TableHeader和TableFooter 我猜你希望的效果是这样的 我猜你希望的效果是这样的 自定义页眉视图 让我们创建一个文件名 UITableViewHeaderFooterView 的 CustomerHeaderView 子类。 现在让我们创建视图的 Xib 文件并将其命名为 CustomHeaderView。 更改高度标…

Spark大数据分析与实战笔记(第一章 Scala语言基础-1)

文章目录 章节概要1.1 初识Scala1.1.1 Scala的概述1.1.2 Scala的下载安装1.1.3 在IDEA开发工具中下载安装Scala插件1.1.4 开发第一个Scala程序 章节概要 Spark是专为大规模数据处理而设计的快速通用的计算引擎&#xff0c;它是由Scala语言开发实现的&#xff0c;关于大数据技术…

Python数据分析实战-找出两个列表中的不同元素(附源码和实现效果)

实现功能 使用 Python 的集合操作来实现找出两个列表中的不同元素。将两个列表转化为集合类型&#xff0c;然后使用集合的操作来找出不同的元素。 实现代码 list1 [1, 2, 3, 4, 5] list2 [3, 4, 5, 6, 7]set1 set(list1) set2 set(list2)diff set1.symmetric_difference…

ssl卸载原理

SSL卸载&#xff0c;也称为SSL解密&#xff0c;是一种将SSL加密数据流卸成非加密的明文数据流的过程。SSL卸载通常在负载均衡器、代理服务器、WAF等设备中实现&#xff0c;可以提高传输效率和安全性。 SSL卸载的原理是将SSL数据流拦截下来&#xff0c;通过设备内置的证书进行解…

Ansible学习笔记(一)

1.什么是Ansible 官方网站&#xff1a;https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html Ansible是一个配置管理和配置工具&#xff0c;类似于Chef&#xff0c;Puppet或Salt。这是一款很简单也很容易入门的部署工具&#xff0c;它使用SS…

《基础教育论坛》期刊简介及投稿要求

《基础教育论坛》杂志是经国家新闻出版总署批准、国内外公开发行的综合性教育学术期刊。作者可通过&#xff0c;中华人民共和国新闻出版总署的网站进行出版许可及刊号的查询。 2009年&#xff0c;《基础教育论坛&#xff08;综合版&#xff09;》杂志创刊。 2012年&#xff0…

HarmonyOS元服务开发实践:桌面卡片字典

HarmonyOS元服务开发实践&#xff1a;桌面卡片字典 本文转载分享自华为开发者论坛《HarmonyOS元服务开发实践&#xff1a;桌面卡片字典》&#xff0c;作者&#xff1a;蛟龙腾飞 一、项目说明 1.DEMO创意为卡片字典。 2.不同卡片显示不同内容&#xff1a;微卡、小卡、中卡、大卡…

框架分析(4)-Spring

框架分析&#xff08;4&#xff09;-Spring 专栏介绍Spring核心特点控制反转&#xff08;IoC&#xff09;面向切面编程&#xff08;AOP&#xff09;组件化集成简化开发总结 优缺点优点高度可扩展控制反转&#xff08;IoC&#xff09;面向切面编程&#xff08;AOP&#xff09;集…

UE学习记录03----UE5.2 使用MVVM示例

1.打开ue5.2新建C项目 2.项目中通过类导向新建C类&#xff0c;父类选择为UMVVMViewModelBase&#xff0c;创建完成会自动打开vs 3.在VS中对新建的类进行宏定义 使用 C 类向导 创建的类声明自动通过 UCLASS() 宏进行处理。 UCLASS() 宏使得引擎意识到这个类的存在&#xff0c;并…

期权分仓开户资金是否安全?具体保障措施有哪些?

网上关于期权分仓系统的真假一直都没有定论&#xff0c;两方人的争论也让很多没有接触过期权分仓系统的人摸不着头脑&#xff0c;那么期权分仓靠谱吗&#xff1f;资金在里面安全吗&#xff1f;下文为大家科普期权分仓开户资金是否安全?具体保障措施有哪些&#xff1f; 一、期权…

站点平台技术架构

系统架构部署思维导图 平台模块分配&#xff1a; 1.账号模块 2.权限模块 3.站点模块 4.配置模块 5.系统升级 6.日志模块 一、前期工作 1.系统保持一致性方案&#xff1a; GIT版本控制&#xff1a;通过总控端向租户端发送一个更新同步请求&#xff0c;租户端收到请求后执行GI…

房屋结构健康监测,科技助力让建筑更安全

房屋建筑是人们赖以生存的场所&#xff0c;然而当前我国许多房屋已经达到了使用寿命的中期&#xff0c;房屋的安全系数逐年降低&#xff0c;风险也随着时间的推移而累积。长期以来&#xff0c;我国的房屋普遍存在寿命短、隐患多的问题&#xff0c;“重建设&#xff0c;轻管理”…

【3维视觉】网格的谱分解和应用(GFT图傅里叶变换)

网格的谱分解即网格的频率分解&#xff0c;我们学过信号的傅里叶变换&#xff0c;将信号从空域变换到频域。二维图像由离散傅里叶变换DFT(Discrete Fourier Transform)。在图信号领域&#xff0c;也有图的傅里叶变换GFT(Graph Fourier Transform)&#xff0c;网格可以看作是图&…

DevExpress WinForms数据编辑器组件,提供丰富的数据输入样式!(二)

DevExpress WinForms超过80个高影响力的WinForms编辑器和多用途控件&#xff0c;从屏蔽数据输入和内置数据验证到HTML格式化&#xff0c;DevExpress数据编辑库提供了无与伦比的数据编辑选项&#xff0c;包括用于独立数据编辑或用于容器控件(如Grid, TreeList和Ribbon)的单元格。…

关于slot-scope已经废弃的问题

说起来啊&#xff0c;这个问题啊&#xff0c;我之前一直没关注&#xff0c;还是webstorm给我的警告。 因为使用了element-ui的组件库&#xff0c;所以在使用组件的时候往往就cv大法了&#xff0c;直到今天用webstorm写代码是&#xff0c;提示了如下的错误 我这一看&#xff0c…

Springboot中sharding-jdbc的API模式并使用自定义算法

Springboot中sharding-jdbc的API模式并使用自定义算法 可配合AbstractRoutingData使用切换数据源 程序用到了AbstractRoutingData来切换数据源&#xff08;数据源是自定义的格式编写并没有用springboot的自动装配的格式写&#xff09;&#xff0c;但是又用到sharding-jdbc进行…

基于微信小程序+Springboot校园二手商城系统设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、目前专注于大学生项目实战开发,讲解,毕业答疑辅导✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3…

【Java 高阶】一文精通 Spring MVC - 数据格式化器(六)

&#x1f449;博主介绍&#xff1a; 博主从事应用安全和大数据领域&#xff0c;有8年研发经验&#xff0c;5年面试官经验&#xff0c;Java技术专家&#xff0c;WEB架构师&#xff0c;阿里云专家博主&#xff0c;华为云云享专家&#xff0c;51CTO 专家博主 ⛪️ 个人社区&#x…

接口经典题目

​ White graces&#xff1a;个人主页 &#x1f649;专栏推荐:《Java入门知识》&#x1f649; &#x1f649; 内容推荐:继承与组合&#xff1a;代码复用的两种策略&#x1f649; &#x1f439;今日诗词:人似秋鸿来有信&#xff0c;事如春梦了无痕。&#x1f439; 目录 &…