Redis布隆过滤器

简介

布隆过滤器(Bloom Filter)是 1970 年由布隆提出的,是一种非常节省空间的概率数据结构,运行速度快,占用内存小,但是有一定的误判率且无法删除元素。它实际上是一个很长的二进制向量和一系列随机映射函数组成,主要用于判断一个元素是否在一个集合中。

Guava 中的布隆过滤器仅使用于单机环境,不使用于分布式环境,分布式环境

模块加载

1、点击https://redis.io/modules 找到RedisBloom。 实际地址:https://github.com/RedisBloom/RedisBloom

2、点击进去下载RedisBloom-master.zip文件,上传到linux

3、解压缩刚才的RedisBloom文件

unzip RedisBloom-master.zip
cd RedisBloom-master
#编译安装
make
#make完生成redisbloom.so,拷贝到redis的安装目录。
cp redisbloom.so /home/www/server/redis

4、在redis.conf配置文件中加入RedisBloomredisbloom.so文件的地址

loadmodule /home/www/server/redis/redisbloom.so

如果是集群每个配置文件中都需要加入redisbloom.so文件的地址

5、或者在启动时指定 module。

redis-server redis.conf --loadmodule /home/www/server/redis/redisbloom.so

重启Redis,在本地和测试环境还可以,但是正式环境能不重启就不需要重启,那这么做可以不重启Redis,使用module load命令执行。

MODULE LOAD /home/www/server/redis/redisbloom.so
module list

命令介绍

参考:https://redis.io/docs/data-types/probabilistic/bloom-filter/

创建BloomFilter

使用给定的期望错误率和初始容量创建空的布隆过滤器

BF.RESERVE {key} {error_rate} {capacity} [EXPANSION expansion] [NONSCALING]

参数说明

  • key:布隆过滤器的key
  • error_rate:期望的错误率(False Positive Rate),该值必须介于0和1之间。该值越小,BloomFilter的内存占用量越大,CPU使用率越高
  • capacity:布隆过滤器的初始容量,即期望添加到布隆过滤器中的元素的个数。当实际添加的元素个数超过该值时,布隆过滤器将进行自动的扩容,该过程会导致性能有所下降,下降的程度是随着元素个数的指数级增长而线性下降
  • EXPANSION:元素超过capacity,扩展。即新容器的capacity是旧容器的capacity的多少倍。默认值是2.
  • NONSCALING:元素超过capacity,不扩展。

返回值

  • 成功:OK
  • 其它情况返回相应的异常信息

增加元素

BF.ADD   {key}  {element}
BF.MADD {key}  {element}  {element}  {element}
  • BF.ADD 返回值:

    • 元素不存在插入成功:返回1
    • 元素可能已经存在:返回0
    • 其它情况返回相应的异常信息
  • BF.MADD返回值:

    • 成功:返回一个数组,数组的每一个元素可能为1或0,当item一定不存在时数组元素值为1,当item可能已经存在时数组元素值为0
    • 其它情况返回相应的异常信息

判断是否存在

 BF.EXISTS  {key}  {element}BF.MEXISTS   {key}  {element} {element} {element}
  • BF.EXISTS返回值:
    • 元素一定不存在:0
    • 元素可能存在:1
    • 其它情况返回相应的异常信息
  • BF.MEXISTS返回值:返回一个数组。
    • 元素一定不存在:0
    • 元素可能存在:1
    • 其它情况返回相应的异常信息

插入元素

向key指定的Bloom中添加多个元素,添加时可以指定大小和错误率,且可以控制在Bloom不存在的时候是否自动创建

bf.insert{key} [CAPACITY {cap}] [ERROR {ERROR}] [NOCREATE] ITEMS {item…}

增量持久化

用于对布隆过滤器的增量保存,这适用于不能正常保存和恢复的大型布隆过滤器,第一次调用此命令时,iter的值应为 0。此命令返回连续(iter, data)对,直到(0, NULL)指示完成。

bf.scandump {key} {item}
  • 参数说明:
    • key:布隆过滤器的名字
    • item:首次调用传值0,或者上次调用此命令返回的结果值

加载数据

加载增量保存的布隆过滤器数据。

BF.LOADCHUNK {key} {iter} {data}

其中iterdata是每次执行BF.SCANDUMP返回的结果值

参考:https://redis.io/commands/bf.scandump/

查看信息

返回布隆过滤器的相关信息

bf.info {key}
  • 返回值:
    • Capacity:预设容量
    • Size:实际占用情况,但如何计算待进一步确认
    • Number of filters:过滤器层数
    • Number of items inserted:已经实际插入的元素数量
    • Expansion rate:子过滤器扩容系数(默认2)

debug

查看布隆过滤器的内部详细信息

bf.debug  {key}

返回值:

  • size:布隆过滤器中已插入的元素数量
  • 每层BloomFilter的详细信息
    • bytes:占用字节数量
      bits:占用bit位数量,bits = bytes * 8
    • shashes:该层hash函数数量
    • hashwidth:hash函数宽度
    • capacity:该层容量(第一层为BloomFilter初始化时设置的容量,第2层容量 = 第一层容量 * expansion,以此类推)
    • size:该层中已插入的元素数量(各层size之和等于BloomFilter中已插入的元素数量size)
    • ratio:该层错误率(第一层的错误率 = BloomFilter初始化时设置的错误率 * 0.5,第二层为第一层的0.5倍,以此类推,ratio与expansion无关)

Java集成Redis使用布隆过滤器

1、redisson实现

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

代码示例1:

Config config = new Config();
SingleServerConfig singleServerConfig = config.useSingleServer();
singleServerConfig.setAddress("redis://127.0.0.1:6379");
singleServerConfig.setPassword("123456");
RedissonClient redissonClient = Redisson.create(config);
RBloomFilter<String> bloom = redissonClient.getBloomFilter("name");
// 初始化布隆过滤器;  大小:100000,误判率:0.01
bloom.tryInit(100000L, 0.01);
for(int i=0;i<100000;i++) {bloom.add("name" + i);
}
boolean notExist = bloom.contains("name1");
if (notExist) {notExistList.add(str);
}

代码示例2:


@RunWith(SpringRunner.class)
@SpringBootTest
public class BloomFilterTest {@Autowiredprivate RedissonClient redissonClient;@Testpublic void testAll() {String key ="BF:AAB";RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter(key);bloomFilter.tryInit(500,0.01);bloomFilter.add("happy");bloomFilter.add("cool");boolean aNew = bloomFilter.contains("new");System.out.println(aNew);aNew =   bloomFilter.contains("cool");System.out.println(aNew);}
}

注意:

#如果不执行 bloom.tryInit(100000L, 0.01); 进行初始化,则会抛出以下异常:
Bloom filter is not initialized!
# Redisson 创建的bloomFilter 内部类型为String。不能操作使用bf.RESERVE 生成的key
WRONGTYPE Operation against a key holding the wrong kind of value. channel: [id: 0x155aa9f1, L:/192.168.8.55:50887 - R:/192.168.1.54:6378] command: (SETBIT), params: [BF:AAA, 14, 1]127.0.0.1:6379> type BF:AAA
MBbloom--
127.0.0.1:6379> type BF:AAB
string

2、com.redislabs实现

        <!-- https://mvnrepository.com/artifact/com.redislabs/jrebloom --><dependency><groupId>com.redislabs</groupId><artifactId>jrebloom</artifactId><version>2.2.2</version></dependency>
@Configurationpublic class RedisBloomFilterConfiguration {@Autowiredprivate RedisTemplate redisTemplate;@Beanpublic JedisPool jedisPool() {RedisConnectionFactory connectionFactory = redisTemplate.getConnectionFactory();if (connectionFactory instanceof LettuceConnectionFactory) {LettuceConnectionFactory lettuceConnectionFactory = (LettuceConnectionFactory) connectionFactory;return new JedisPool(new GenericObjectPoolConfig(), lettuceConnectionFactory.getHostName(), lettuceConnectionFactory.getPort(), Long.valueOf(lettuceConnectionFactory.getTimeout()).intValue(), lettuceConnectionFactory.getPassword(), lettuceConnectionFactory.getDatabase());} else {if (connectionFactory instanceof JedisConnectionFactory) {JedisConnectionFactory jedisConnectionFactory = (JedisConnectionFactory) connectionFactory;return new JedisPool((jedisConnectionFactory).getPoolConfig());}}throw new IllegalArgumentException("RedisConnectionFactory unsupport.");}
}

import io.rebloom.client.Client;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;
import redis.clients.jedis.JedisPool;@RunWith(SpringRunner.class)
@SpringBootTest
public class BloomFilter2Test {@Autowiredprivate RedisTemplate redisTemplate;@Autowiredprivate JedisPool jedisPool;@Testpublic void testAll() {String key = "BF:AAE";Client client = new Client(jedisPool);Boolean exists = redisTemplate.hasKey(key);if (!exists) {client.createFilter(key, 1000, 0.01);}client.add(key, "happy");client.add(key, "cool");boolean aNew = client.exists(key, "new");System.out.println(aNew);aNew = client.exists(key, "cool");System.out.println(aNew);}
}
127.0.0.1:6379> type BF:AAE
MBbloom--

3、redistemplate + lua


import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;import java.util.Arrays;
import java.util.List;/*** RedisBloomFilterUtil* RedisTemplate实现方式** @author lihz* @date 2023/12/5*/
public class RedisBloomFilterUtil {private static RedisScript<Boolean> SCRIPT_RESERVE = new DefaultRedisScript<>("return redis.call('bf.reserve', KEYS[1], ARGV[1], ARGV[2])", Boolean.class);private static RedisScript<Boolean> SCRIPT_ADD = new DefaultRedisScript<>("return redis.call('bf.add', KEYS[1], ARGV[1])", Boolean.class);private static RedisScript<Boolean> SCRIPT_EXISTS = new DefaultRedisScript<>("return redis.call('bf.exists', KEYS[1], ARGV[1])", Boolean.class);private static String SCRIPT_MADD = "return redis.call('bf.madd', KEYS[1], %s)";private static String SCRIPT_MEXISTS = "return redis.call('bf.mexists', KEYS[1], %s)";private RedisTemplate redisTemplate;public RedisBloomFilterUtil(RedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;}/*** 设置错误率和大小(需要在添加元素前调用,若已存在元素,则会报错)* 错误率越低,需要的空间越大** @param key* @param errorRate   错误率,默认0.01* @param initialSize 默认100,预计放入的元素数量,当实际数量超出这个数值时,误判率会上升,尽量估计一个准确数值再加上一定的冗余空间* @return*/public Boolean reserve(String key, double errorRate, int initialSize) {return (Boolean) redisTemplate.<Boolean>execute(SCRIPT_RESERVE, Arrays.asList(key), String.valueOf(errorRate),String.valueOf(initialSize));}/*** 添加元素** @param key* @param value* @return true表示添加成功,false表示添加失败(存在时会返回false)*/public Boolean add(String key, String value) {return (Boolean) redisTemplate.<Boolean>execute(SCRIPT_ADD, Arrays.asList(key), value);}/*** 查看元素是否存在(判断为存在时有可能是误判,不存在是一定不存在)** @param key* @param value* @return true表示存在,false表示不存在*/public Boolean exists(String key, String value) {return (Boolean) redisTemplate.<Boolean>execute(SCRIPT_EXISTS, Arrays.asList(key), value);}/*** 批量添加元素** @param key* @param values* @return 按序 1表示添加成功,0表示添加失败*/public List<Integer> madd(String key, List<String> values) {String[] vs = new String[values.size()];values.toArray(vs);return (List<Integer>) redisTemplate.execute(this.generateScript(SCRIPT_MADD, vs), Arrays.asList(key),vs);}/*** 批量检查元素是否存在(判断为存在时有可能是误判,不存在是一定不存在)** @param key* @param values* @return 按序 1表示存在,0表示不存在*/public List<Integer> mexists(String key, List<String> values) {String[] vs = new String[values.size()];values.toArray(vs);return (List<Integer>) redisTemplate.execute(this.generateScript(SCRIPT_MEXISTS, vs), Arrays.asList(key), vs);}private RedisScript<List> generateScript(String script, String[] values) {StringBuilder sb = new StringBuilder();for (int i = 1; i <= values.length; i++) {if (i != 1) {sb.append(",");}sb.append("ARGV[").append(i).append("]");}return new DefaultRedisScript<>(String.format(script, sb.toString()), List.class);}
}public class BloomFilter2Test {@Autowiredprivate RedisTemplate redisTemplate;@Autowiredprivate JedisPool jedisPool;public void testAll() {String key = "BF:AAH";StringRedisSerializer serializer = new StringRedisSerializer();redisTemplate.setKeySerializer(serializer);redisTemplate.setValueSerializer(serializer);RedisBloomFilterUtil util = new RedisBloomFilterUtil(redisTemplate);Boolean exists = redisTemplate.hasKey(key);if (!exists) {util.reserve(key, 0.01, 1000);}util.add(key, "happy");util.add(key, "cool");boolean aNew = util.exists(key, "new");System.out.println(aNew);aNew = util.exists(key, "cool");System.out.println(aNew);util.madd(key, Arrays.asList("rich", "niubi", "hot", "cry"));List<Integer> mexists = util.mexists(key, Arrays.asList("cool", "swarm"));System.out.println(mexists);mexists = util.mexists(key, Arrays.asList("cry", "xxx", "hot"));System.out.println(mexists);}}   

区别

RedisBloomRedisson实现的过滤器区别:

  • 数据结构: RedisBloom相当于为了实现过滤器而新增了一个数据结构,而Redisson是基于redis原有的bitmap位图数据结构来通过硬编码实现的过滤器。
  • 存储: 存储两者其实并没有差距,都没有存储原数据。
  • 容错: 同样是10000条数据0.01容错,RedisBloom误判元素是58,Redisson实现的是227。
  • 耦合度: 使用RedisBloom就需要安装RedisBloom,如果不安装RedisBloom程序直接就不能使用了,而使用Redisson他只依赖于redis
  • 分片: RedisBloom只是redis一种数据结构,本身redis集群就是支持分片的,所以RedisBloom肯定也没问题,Redisson的布隆过滤器也支持分片,但是需要付费。
  • 性能: 使用 redis 的位图来实现的布隆过滤器性能上要差不少。比如一次 exists 查询会涉及到多次 getbit 操作,网络开销相比而言会高出不少。

附录

参考

https://redis.com/blog/bloom-filter/

https://github.com/redis/jedis

spring-data-redis与Jedis版本: https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis

jedis与redis对应版本

Jedis versionSupported Redis versionsJDK Compatibility
3.9+5.0 and 6.2 Family of releases8, 11
>= 4.0Version 5.0 to current8, 11, 17
>= 5.0Version 6.0 to current8, 11, 17

免费的在线布隆过滤器在线计算

https://hur.st/bloomfilter/?n=1000000&p=0.03&m=&k=

示例

127.0.0.1:6379> BF.RESERVE BF:AAA 0.01 50 
OK
127.0.0.1:6379> BF.ADD   BF:AAA  abort
(integer) 1
127.0.0.1:6379> BF.ADD   BF:AAA  about
(integer) 1
127.0.0.1:6379> BF.MADD BF:AAA  happy  cool
1) (integer) 1
2) (integer) 1
127.0.0.1:6379> BF.EXISTS BF:AAA  abort
(integer) 1
127.0.0.1:6379> BF.EXISTS BF:AAA  pool
(integer) 0
127.0.0.1:6379> BF.MEXISTS BF:AAA abort pool rich happy
1) (integer) 1
2) (integer) 0
3) (integer) 0
4) (integer) 1
127.0.0.1:6379> BF.INFO BF:AA
(error) ERR not found
127.0.0.1:6379> BF.INFO BF:AAA1) Capacity2) (integer) 503) Size4) (integer) 1685) Number of filters6) (integer) 17) Number of items inserted8) (integer) 49) Expansion rate
10) (integer) 2
127.0.0.1:6379> 
127.0.0.1:6379> bf.debug  BF:AAA
1) "size:4"
2) "bytes:72 bits:576 hashes:8 hashwidth:64 capacity:50 size:4 ratio:0.005"

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

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

相关文章

报错解析:apt-get install curl -y‘ returned a non-zero code: 100

错误回顾 The command ‘/bin/sh -c apt-get update && apt-get install curl -y’ returned a non-zero code: 100 docker build 时想给容器加上curl命令&#xff0c;执行报错 dockerfile命令 FROM Ubuntu20.04#RUN apt-get update && apt-get install -y…

Android Studio下载gradle失败

1、打开Android Studio设置Gradle的地方&#xff0c;点击左上角的File->Settings查看gradle存放路径 C:\Users\Administrator.gradle\wrapper\dists\gradle-5.4.1-all\3221gyojl5jsh0helicew7rwx 2、找到正在下载的gradle版本&#xff0c;Android Studio取消下载gradle&…

Tomcat与Servlet是什么关系

Tomcat与Servlet是什么关系 Apache Tomcat和Servlet之间存在密切的关系&#xff0c;可以说它们是一对密切合作的组件。下面是它们的关系&#xff1a; Tomcat是Servlet容器&#xff1a; Tomcat是一个开源的、轻量级的Servlet容器。Servlet容器是一个Web服务器扩展&#xff0c;用…

2023.12.28 关于 Redis 数据类型 List 内部编码、应用场景

目录 List 编码方式 早期版本 现今版本 List 实际应用 多表之间的关联关系 消息队列 频道&#xff08;多列表&#xff09;消息队列 微博 Timeline 栈 & 队列 List 编码方式 早期版本 早期版本 List 类型的内部编码方式有两种 ziplist&#xff08;压缩列表&#xf…

C语言中数组和指针的sizeof和strlen详解

数组和指针是常用的数据类型&#xff0c;了解它们的sizeof和strlen的使用是编写高效程序的关键。本文将通过一系列示例代码详细解释数组和指针在不同场景下的sizeof和strlen的计算方式。 一维数组 首先&#xff0c;我们来看一维数组的情况&#xff1a; int main() {int a[] …

Cisco模拟器-企业网络部署

某企业园区网有&#xff1a;2个分厂&#xff08;分别是&#xff1a;零件分厂、总装分厂&#xff09;1个总厂网络中心 1个总厂会议室&#xff1b; &#xff08;1&#xff09;每个分厂有自己的路由器&#xff0c;均各有&#xff1a;1个楼宇分厂网络中心 每个楼宇均包含&#x…

004、变量与可变性

1. 变量与可变性 在Rust中&#xff0c;变量默认是不可变的&#xff0c;这一设计是为了让你安全方便地写出复杂、甚至是并行的代码。 当然&#xff0c;Rust也提供了可使用的可变变量的方法&#xff0c;这个待会讨论。 当一个变量是不可变时&#xff0c;一旦它被绑定到某个值上面…

Java之程序、进程、线程、管程和并发、并行的概念

文章目录 1. 进程与线程1.1 程序1.2 进程1.3 线程1.4 管程 2.并行与并发2.1 并发2.2 并行 1. 进程与线程 1.1 程序 程序是指令和数据的有序集合&#xff0c;其本身没有任何运行的含义&#xff0c;是一个静态的概念。简单的说就是我们写的代码。 1.2 进程 &#xff08;1&…

分布式系统架构设计之分布式数据存储的分类和组合策略

在现下科技发展迅猛的背景下&#xff0c;分布式系统已经成为许多大规模应用和服务的基础架构。分布式架构的设计不仅仅是一项技术挑战&#xff0c;更是对数据存储、管理和处理能力的严峻考验。随着云原生、大数据、人工智能等技术的崛起&#xff0c;分布式系统对于数据的高效存…

MySQL一些常用命令

1、登录本地MySQL #一种是 mysql -u root -p; #(输入密码后回车)#另一种是 mysql -uroot -p123456; #(在-p后面直接带上密码)2、启动MySQL服务 net start mysql; 3、关闭MySQL服务&#xff1a; net stop mysql; 4、创建数据库 create database 数据库名; 5、创建数据…

科技云报道:开源才是大模型的未来?

科技云报道原创。 一年前&#xff0c;ChatGPT横空出世&#xff1b;7个多月后&#xff0c;Meta宣布开源LLaMA 2&#xff0c;并且可免费商用。 这一天&#xff0c;也成为大模型发展的分水岭。短时间内&#xff0c;LLaMA 2对一些闭源的大模型厂商造成了致命性的打击。 随后&…

【力扣100】207.课程表

添加链接描述 class Solution:def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:# 思路是计算每一个课的入度&#xff0c;然后使用队列进行入度为0的元素的进出# 数组&#xff1a;下标是课程号&#xff0c;array[下标]是这个课程的入度# 哈希…

轻松调整视频时长,创意与技术的新篇章

传统的视频剪辑工具往往难以精确控制时间&#xff0c;而【媒体梦工厂】凭借其先进的算法和界面设计&#xff0c;让视频时长的调整变得简单而精确&#xff0c;助你释放无限的创意&#xff0c;用技术为你的创意插上翅膀&#xff0c;让每一秒都有意义。 所需工具&#xff1a; 一…

性能优化-如何提高cache命中率

本文主要介绍性能优化领域常见的cache的命中率问题&#xff0c;旨在全面的介绍提高cache命中率的方法&#xff0c;以供大家编写出性能友好的代码&#xff0c;并且可以应对性能优化领域的面试问题。 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &am…

听GPT 讲Rust源代码--src/tools(38)

File: rust/src/tools/clippy/clippy_dev/src/lib.rs rust/src/tools/clippy/clippy_dev/src/lib.rs文件是Clippy开发工具的入口文件&#xff0c;其作用是提供Clippy开发过程中所需的功能和工具。Clippy是一个Rust代码的静态分析工具&#xff0c;用于提供各种有用的代码规范、编…

prometheus-docker 快速安装

镜像加速 sudo mkdir -p /etc/docker sudo tee /ect/docker/daemon.json << "EOF" {"register-mirros": ["http://hub-mirror.c.163.com"] } EOF安装docker export DOWNLOAD_URL"http://mirrors.163.com/docker-ce" curl -fsSl…

C++day4作业

定义一个Person类&#xff0c;私有成员int age&#xff0c;string &name&#xff0c;定义一个Stu类&#xff0c;包含私有成员double *score&#xff0c;写出两个类的构造函数、析构函数、拷贝构造和拷贝赋值函数&#xff0c;完成对Person的运算符重载(算术运算符、条件运算…

dubbo的一些问题思考

1.dubbo是啥 Dubbo 是一个高性能的 Java RPC&#xff08;远程过程调用&#xff09;框架&#xff0c;用于构建分布式服务架构。由阿里巴巴开发并开源&#xff0c;作为一个分布式服务框架&#xff0c;Dubbo 提供了丰富的功能&#xff0c;包括服务治理、远程调用、负载均衡、容错机…

kafka-python简单生产消费数据

kafka-python使用手册 kafka-python 1. 生产者同步发送数据 # 生产者同步发送数据from kafka import KafkaProducer from kafka.errors import KafkaErrorproducer KafkaProducer(bootstrap_servers["192.168.1.6:9092"])try:record_metadata producer.send(&qu…

java企业网站系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java Web企业网站系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0&…