redis布隆过滤器(Bloom)详细使用教程

news2024/11/16 21:25:40

文章目录

  • 布隆过滤器
    • 1. 原理
    • 2. 结构和操作
    • 3. 特点和应用场景
    • 4. 缺点和注意事项
  • 应用-redis插件布隆过滤器使用详细过程
    • 安装以及配置
    • springboot项目使用redis布隆过滤器
      • 下面是布隆过滤器的一些基础命令
    • 扩展

布隆过滤器

Bloom 过滤器是一种概率型数据结构,用于快速判断一个元素是否属于一个集合。它以较小的空间占用和高效的查询时间著称。下面将对 Bloom 过滤器进行详细阐述。

1. 原理

Bloom 过滤器基于哈希函数和位数组实现。它的核心思想是使用多个哈希函数将元素映射到位数组中,并将对应的位设置为1。当查询一个元素时,通过对该元素进行相同的哈希计算,检查对应的位是否都为1。如果其中有任何一位为0,则可以确定该元素不在集合中;如果所有位都为1,则该元素可能在集合中,但并不确定,存在一定的概率误判。

2. 结构和操作

  • 位数组(Bit Array):Bloom 过滤器使用一个固定长度的位数组来表示集合,并初始化为全0。每个元素通过多个哈希函数映射到位数组上的多个位置。
  • 哈希函数(Hash Function):Bloom 过滤器使用多个独立的哈希函数,每个哈希函数可以将一个元素映射到位数组的不同位置。常用的哈希函数包括 MurmurHash、FnvHash、SHA 等。
  • 添加元素(Add Element):当向 Bloom 过滤器中添加一个元素时,将该元素经过多个哈希函数的计算得到的位置对应的位设置为1。
  • 查询元素(Query Element):当查询一个元素时,通过多个哈希函数计算出对应的位置,并检查这些位置上的位是否都为1。如果有任何一位为0,则可以确定该元素不在集合中;如果所有位都为1,则该元素可能在集合中。

3. 特点和应用场景

  • 空间效率高:Bloom 过滤器使用位数组表示集合,所需的内存空间相对较小,与集合大小无关。
  • 查询效率高:由于只需计算多个哈希函数并检查位数组上的位,查询时间较短,通常为常数时间复杂度。
  • 概率误判:Bloom 过滤器在判断一个元素不在集合中时,永远是准确的;但在判断一个元素在集合中时,存在一定的概率误判。误判率取决于哈希函数的个数和位数组的大小。
  • 应用场景:Bloom 过滤器适用于需要快速判断元素是否属于一个大规模集合的场景,如网页爬虫中的 URL 去重、缓存穿透的防护、垃圾邮件过滤等。

4. 缺点和注意事项

  • 无法删除元素:Bloom 过滤器的位数组一旦被置为1,就无法撤销。因此,无法从 Bloom 过滤器中删除元素。
  • 哈希函数选择:选择合适的哈希函数和哈希函数的数量非常重要。哈希函数应具有较低的冲突率,并且应该尽量使用独立性较强的哈希函数。
  • 误判率:误判率取决于哈希函数的个数和位数组的大小。通过调整这些参数可以降低误判率,但也会增加空间占用和查询时间。
  • 适用范围:Bloom 过滤器适用于对查询时间和空间占用有较高要求,而对概率误判可以接受的场景。在对精确性要求较高的情况下,Bloom 过滤器可能不适用。

总而言之,Bloom 过滤器是一种高效的概率型数据结构,通过位数组和多个哈希函数实现快速的集合元素判断。它在一些特定的应用场景中具有很大的优势,但需要注意选择合适的哈希函数和参数设置,以及理解概率误判的特性。

应用-redis插件布隆过滤器使用详细过程

安装以及配置

布隆过滤器有很多,我这里用的redis提供的布隆过滤器,这次使用的是用docker安装的redis以及配置布隆过滤器

1. 首先下载布隆过滤器这个插件

wget https://github.com/RedisLabsModules/rebloom/archive/v2.2.6.tar.gz

下载以后解压备用一会等着放到redis中
2.docker安装redis
首先创建文件夹以及配置文件,用于挂在redis启动的后容器中的文件,方便我们在容器外部操作redis的配置
创建文件夹

mkdir data  ##创建文件
touch redis.conf  ## 创建文件

在创建完文件夹以后将我们第一步中下载并解压好的布隆过滤器的文件夹放到我们创建的data文件夹下
在这里插入图片描述
在我们创建的redis.conf文件中添加一行配置loadmodule /data/RedisBloom-2.2.6/redisbloom.so
在这里插入图片描述

随后直接使用dokcer run命令进行启动,如果没有安装redis则进行下载

docker run -p 6379:6379 --name redis -v /root/redis/data:/data -v /root/redis/redis.conf:/etc/redis/redis.conf --restart=always --network host  -d redis:5.0.7 redis-server /etc/redis/redis.conf

这个命令是用于在 Docker 中运行 Redis 容器,并进行一些配置。下面是对每个参数的解释:

  • -p 6379:6379: 将 Docker 容器的端口 6379 映射到主机的端口 6379,以便可以从主机访问 Redis 服务。
  • --name redis: 指定容器的名称为 “redis”。
  • -v /root/redis/data:/data: 将主机的 /root/redis/data 目录挂载到容器的 /data 目录,用于持久化保存 Redis 数据。
  • -v /root/redis/redis.conf:/etc/redis/redis.conf: 将主机的 /root/redis/redis.conf 配置文件挂载到容器的 /etc/redis/redis.conf,使用该配置文件作为 Redis 的配置。
  • --restart=always: 设置容器在退出时自动重新启动。
  • --network host: 使用主机网络模式,容器将共享主机的网络栈。
  • -d: 在后台运行容器。
  • redis:5.0.7: 指定使用的 Redis 镜像及其版本号。
  • redis-server /etc/redis/redis.conf: 在容器中执行的命令,即启动 Redis 服务器,并使用指定的配置文件。

执行上述操作redis容器如果启动没有问题那么我们的布隆过滤器的插件和redis都安装并启动成功了,如果没有启动成功可以通过docker logs 查看一下redis的启动过程中出现什么问题

下面连接redis执行下面的代码查看否布隆过滤器安装成功

bf.add user test

解释一下:bf.add 是安装布隆过滤器后才可以使用的命令,这是添加一个key的命令,user是过滤器的名字,而tese就是我们要去添加的key
在这里插入图片描述
这是添加成功的标识。

springboot项目使用redis布隆过滤器

上面我们把布隆过滤器安装成功了,那么下面介绍一下在项目中如何应用这个过滤器如何通过代码来去和过滤器交互
我这里使用的redis的过滤器所以用到的依赖直接使用的spring-data-redis这个就可以了

        <!--redis的依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

引入依赖以后我们配置封装一个用于调用过滤器的工具类

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@Component
public class RedisBloomUtil {
    @Autowired
    private RedisTemplate redisTemplate;
    // 初始化一个布隆过滤器
    public Boolean tryInitBloomFilter(String key, long expectedInsertions, double falseProbability) {
        Boolean keyExist = redisTemplate.hasKey(key);
        if(keyExist) {
            return false;
        }
        RedisScript<Boolean> script = new DefaultRedisScript<>(bloomInitLua(), Boolean.class);
        RedisSerializer stringSerializer = redisTemplate.getStringSerializer();
        redisTemplate.execute(script, stringSerializer, stringSerializer, Collections.singletonList(key), falseProbability+"", expectedInsertions+"");
        return true;
    }
    // 添加元素
    public Boolean addInBloomFilter(String key, Object arg) {
        RedisScript<Boolean> script = new DefaultRedisScript<>(addInBloomLua(), Boolean.class);
        return (Boolean) redisTemplate.execute(script, Collections.singletonList(key), arg);
    }
    @Transactional
    // 批量添加元素
    public Boolean batchAddInBloomFilter(String key, Object... args) {
        RedisScript<Boolean> script = new DefaultRedisScript<>(batchAddInBloomLua(), Boolean.class);
        return (Boolean) redisTemplate.execute(script, Collections.singletonList(key), args);
    }
    // 查看某个元素是否是存在
    public Boolean existInBloomFilter(String key, Object arg) {
        RedisScript<Boolean> script = new DefaultRedisScript<>(existInBloomLua(), Boolean.class);
        return (Boolean) redisTemplate.execute(script, Collections.singletonList(key), arg);
    }
    // 批量查看元素是否存在
    public List batchExistInBloomFilter(String key, Object... args) {
        RedisScript<List> script = new DefaultRedisScript(batchExistInBloomLua(), List.class);
        List<Long> results = (List) redisTemplate.execute(script, Collections.singletonList(key), args);
        List<Boolean> booleanList = results.stream().map(res -> res == 1 ? true : false).collect(Collectors.toList());
        return booleanList;
    }


    private String bloomInitLua() {
        return "redis.call('bf.reserve', KEYS[1], ARGV[1], ARGV[2])";
    }
    private String addInBloomLua() {
        return "return redis.call('bf.add', KEYS[1], ARGV[1])";
    }
    private String batchAddInBloomLua() {
        StringBuilder sb = new StringBuilder();
        sb.append("for index, arg in pairs(ARGV)").append("\r\n");
        sb.append("do").append("\r\n");
        sb.append("redis.call('bf.add', KEYS[1], arg)").append("\r\n");
        sb.append("end").append("\r\n");
        sb.append("return true");
        return sb.toString();
    }
    private String existInBloomLua() {
        return "return redis.call('bf.exists', KEYS[1], ARGV[1])";
    }
    private String batchExistInBloomLua() {
        StringBuilder sb = new StringBuilder();
        sb.append("local results = {}").append("\r\n");
        sb.append("for index, arg in pairs(ARGV)").append("\r\n");
        sb.append("do").append("\r\n");
        sb.append("local exist = redis.call('bf.exists', KEYS[1], arg)").append("\r\n");
        sb.append("table.insert(results, exist)").append("\r\n");
        sb.append("end").append("\r\n");
        sb.append("return results;");
        return sb.toString();
    }
}

下面是布隆过滤器的一些基础命令

在 Redis 中,可以使用 RedisBloom 模块来实现布隆过滤器。RedisBloom 是一个开源模块,提供了一系列命令来操作布隆过滤器。下面是 RedisBloom 模块中常用的命令集合:

  1. BF.ADD:向布隆过滤器中添加一个元素。

    BF.ADD <key> <item>
    
  2. BF.EXISTS:检查一个元素是否存在于布隆过滤器中。

    BF.EXISTS <key> <item>
    
  3. BF.MADD:向布隆过滤器中批量添加多个元素。

    BF.MADD <key> <item> [item ...]
    
  4. BF.MEXISTS:批量检查多个元素是否存在于布隆过滤器中。

    BF.MEXISTS <key> <item> [item ...]
    
  5. BF.INFO:获取布隆过滤器的信息,包括容量、误判率等。

    BF.INFO <key>
    
  6. BF.RESERVE:创建一个新的布隆过滤器,并指定容量和误判率。

    BF.RESERVE <key> <error_rate> <capacity>
    
  7. BF.COUNT:统计布隆过滤器中已添加的元素数量。

    BF.COUNT <key>
    
  8. BF.DEBUG:调试命令,用于打印布隆过滤器内部的一些调试信息。

    BF.DEBUG <subcommand> [arguments ...]
    

我上面提供的工具类就是封装的这些命令。

扩展

关于布隆过滤器我们在使用的是注意点,就是在我上面说到的测试一下是否安装成功时使用的添加数据的命令,bf.add 过滤器名称 key,但是我并没有创建那个名字为user的过滤器,是因为这是程序帮我创建了一个叫做user的过滤器,这个过滤器的配置都是一些基础的配置,比如初始容量是100 错误率是0.01也就是百分之一的错误率,这个过滤明显不能满足我们的需要因为过滤器的工作原理就是通过多个哈希函数对key进行计算然后记录下来,那么容量就决定了在计算的过程中发生碰撞的概率大小了,所以我们在使用的时候一定要去手动创建过滤器以确保满足自己的需要。
在这里插入图片描述

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

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

相关文章

算法学习——华为机考题库5(HJ31 - HJ35)

算法学习——华为机考题库5&#xff08;HJ31 - HJ35&#xff09; HJ31 单词倒排 描述 对字符串中的所有单词进行倒排。 说明&#xff1a; 1、构成单词的字符只有26个大写或小写英文字母&#xff1b; 2、非构成单词的字符均视为单词间隔符&#xff1b; 3、要求倒排后的单…

Jmeter,如何从数组参数中取值

有个post请求&#xff0c;参数“equipment_ids”&#xff0c;是个数组&#xff0c;需求每次执行的时候&#xff0c;按顺序取equipment_ids中不同的值 要实现在 JMeter 中每次执行请求时按顺序取不同的 equipment_ids 中的值&#xff0c;你可以使用 Counter 元件来生成索引&…

Linux 磁盘空间占用率100%的排查

&#x1f4d1;前言 使用 Linux 操作系统时&#xff0c;可能会遇到磁盘空间不足的错误&#xff0c;这种错误通常会导致系统运行缓慢或崩溃。本文将介绍磁盘排查的方法。⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是青衿&#x1f947; ☁️博客首页&#xff1…

【深入浅出Java性能调优】「底层技术原理体系」详细分析探索Java服务器性能监控Metrics框架的实现原理分析(Dropwizard度量基础案例指南)

深入探索Java服务器性能监控Metrics框架的实现原理分析 前提介绍Dropwizard MetricsDropwizard的特点Dropwizard的开发案例需要引入Maven依赖常用度量类型Meter(每秒请求数为单位测量请求率)定义度量核心MetricRegistry构建对应的Meter指标对象请求标记采样业务方法控制报告器…

【C++入门到精通】C++的IO流(输入输出流) [ C++入门 ]

阅读导航 引言一、C语言的输入与输出二、流是什么三、CIO流1. C标准IO流&#xff08;1&#xff09;istream&#xff08;2&#xff09;ostream&#xff08;3&#xff09;iostream&#xff08;4&#xff09;cin 和 cout 2. C文件IO流&#xff08;1&#xff09;ifstream&#xff0…

WPF简介

WPF早期名称为Avalon&#xff0c;是微软的一个项目&#xff0c;目的是构建统一的平面展示平台 WPF是一个与分辨率无关的UI框架&#xff0c;是一种基于矢量的呈现引擎技术&#xff1b;所用的语言为XAML(全称Extensible Application MarkupLanguage)&#xff0c;它的基本度量单位…

02 使用jdk运行第一个java程序:HelloWorld

使用jdk运行第一个java程序 1 HelloWorld小案例1.1 编写流程1.2 错误示例 首先在CMD命令行里面&#xff0c;使用javac xxxx.java&#xff0c; 进行编译&#xff0c;其中会有报错&#xff1b; 然后生成xxxx.class 文件&#xff0c;然后使用java xxxx.class 进行运行。 1 HelloWo…

回归预测 | Matlab实现POA-CNN-LSTM-Attention鹈鹕算法优化卷积长短期记忆网络注意力多变量回归预测(SE注意力机制)

回归预测 | Matlab实现POA-CNN-LSTM-Attention鹈鹕算法优化卷积长短期记忆网络注意力多变量回归预测&#xff08;SE注意力机制&#xff09; 目录 回归预测 | Matlab实现POA-CNN-LSTM-Attention鹈鹕算法优化卷积长短期记忆网络注意力多变量回归预测&#xff08;SE注意力机制&…

五大浏览器内核及代表浏览器,一文讲透!

Hi,我是贝格前端工场&#xff0c;在进行web前端开发的时候&#xff0c;浏览器兼容性一直是让所有前端工程师头疼的问题&#xff0c;其根源在于不同的浏览器应用了不同的内核&#xff0c;其对html、css、js的解析规则也是不一样的&#xff0c;作为前端开发的你&#xff0c;如果不…

2024美国大学生数学建模E题财产保险的可持续模型详解思路+具体代码季节性时序预测SARIMA天气预测建模

上一篇已经对赛题进行详细分析了&#xff0c;而且大方向和基本的模型已经确定完毕&#xff0c;数据集都已经找到了&#xff0c;现在最重要的就是要分析风暴数据集以及建立时序预测模型&#xff0c;使用气候模型预测的数据&#xff0c;评估气候变化对未来极端天气事件频率和强度…

全面认识DOS系统

目录 一、DOS系统的功能 1.执行命令和程序&#xff08;处理器管理&#xff09; 2.内存管理 3.设备管理 4.文件管理 5.作业管理 二、文件与目录 三、文件类型与属性 1.系统属性&#xff08;S&#xff09; 2.隐含属性&#xff08;H&#xff09; 3.只读属性&#xff08…

深度学习手写字符识别:训练模型

说明 本篇博客主要是跟着B站中国计量大学杨老师的视频实战深度学习手写字符识别。 第一个深度学习实例手写字符识别 深度学习环境配置 可以参考下篇博客&#xff0c;网上也有很多教程&#xff0c;很容易搭建好深度学习的环境。 Windows11搭建GPU版本PyTorch环境详细过程 数…

华为机考入门python3--(6)牛客6-质数因子

分类&#xff1a;质数、素数 知识点&#xff1a; 取余符号% 5%3 2 取整符号// 5//3 1 list中int元素转str map(str, list) 题目来自【牛客】 def prime_factors(n): """ 输入一个正整数n&#xff0c;输出它的所有质因子&#xff08;重复的也…

python pygame实现倒计时

实现思路 获取开始时间、当前时间&#xff0c;通过当前时间-开始时间时间差&#xff0c;再通过倒计时的总时长-时间差即可实现&#xff01; 随着时间的流逝&#xff0c;当前时间会变大&#xff0c;也就导致时间差会变大&#xff0c;当使用总时长-时间差的时候&#xff0c;得到…

基于控制台的购书系统(Java 语言实现)

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》|《数据结构与算法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有限&#xff0c;欢…

【Nginx】nginx入门

文章目录 一、Web服务器二、Nginx三、Nginx的作用Web服务器正向代理反向代理 四、CentOS上安装Nginx(以CentOS 7.9为例) 一、Web服务器 Web 服务器&#xff0c;一般是指“网站服务器”&#xff0c;是指驻留于互联网上某种类型计算机的程序。Web 服务器可以向 Web 浏览器等客户…

[开源]基于野火指南者的MQTT框架+FreeRTOS移植(使用板载esp8266模块)

MQTT移植 项目地址 实际使用 基于野火指南者开发板 移植大佬的MQTT框架, 参考韦东山的课程 实际移植的项目: mqttclient 主要实现的是使用开发板上面的ESP8266连接MQTT服务器, 目前使用的是ip地址进行连接(FreeRTOS版本) 测试程序在mqtt/at/at_comment.c文件里面, 需要改…

Vivado MIG IP使用配置

目录 1 MIG 基本配置 1 MIG 基本配置 配置如下图所示 图1 图2 图3 图4 图5 图6 图8 图9 在设立只讲解共同配置&#xff0c;这是所有DDR3中配置通用部分。

2024年【低压电工】复审考试及低压电工作业考试题库

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 低压电工复审考试参考答案及低压电工考试试题解析是安全生产模拟考试一点通题库老师及低压电工操作证已考过的学员汇总&#xff0c;相对有效帮助低压电工作业考试题库学员顺利通过考试。 1、【单选题】()是保证电气作…

CSS常用动画网站(纯css echarts等 建议经常阅读 积累素材)

CSS动画代码集合 https://www.webhek.com/post/css3-animation-sniplet-collection/#/ 这个网站中将常见的css动画都进行了集合,并且有详细的代码,可以直接使用 echarts图表 https://www.isqqw.com/ echarts也是前端常用的,虽然官方文档已经给出了很多的案例,但是有时候产品还…