找回密码
 立即注册
首页 业界区 业界 Redis布隆过滤器的保姆级教程

Redis布隆过滤器的保姆级教程

屠焘 2026-2-7 14:15:00
以「生产环境首选的 RedisBloom 模块」为核心,兼顾「无模块时的手动 Bitmap 实现方案」,全程步骤拆解到最小单元,新手也能跟着做。
一、前置环境准备

1.1 确保 Redis 已安装 RedisBloom 模块

布隆过滤器的高效实现依赖 RedisBloom 扩展,优先用 Docker 快速部署(新手友好):
  1. # 1. 拉取包含 RedisBloom 的镜像(国内可加镜像源加速)
  2. docker pull redislabs/rebloom:latest
  3. # 2. 启动 Redis 容器(映射端口 6379,设置密码 123456,方便后续配置)
  4. docker run -d --name redis-bloom -p 6379:6379 -e REDIS_PASSWORD=123456 redislabs/rebloom:latest
复制代码
验证 RedisBloom 是否安装成功
  1. # 进入容器
  2. docker exec -it redis-bloom redis-cli
  3. # 输入密码(如果设置了)
  4. 127.0.0.1:6379> AUTH 123456
  5. OK
  6. # 执行 BF.RESERVE 命令,返回 OK 则说明模块正常
  7. 127.0.0.1:6379> BF.RESERVE test_bloom 0.01 10000
  8. OK
复制代码
二、SpringBoot 项目搭建(IDEA 为例)

2.1 创建基础 SpringBoot 项目


  • 打开 IDEA → 新建项目 → 选择「Spring Initializr」→ 填写项目信息(Group/Artifact);
  • 选择依赖:Spring Web + Spring Data Redis(核心依赖);
  • 点击「Finish」完成创建。
2.2 引入核心依赖(pom.xml)

确保 pom.xml 包含以下依赖(版本可根据 SpringBoot 版本适配):
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3.          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4.     <modelVersion>4.0.0</modelVersion>
  5.     <parent>
  6.         <groupId>org.springframework.boot</groupId>
  7.         spring-boot-starter-parent</artifactId>
  8.         <version>2.7.15</version>
  9.         <relativePath/>
  10.     </parent>
  11.    
  12.     <groupId>com.example</groupId>
  13.     springboot-bloomfilter</artifactId>
  14.     <version>0.0.1-SNAPSHOT</version>
  15.    
  16.     <dependencies>
  17.         
  18.         <dependency>
  19.             <groupId>org.springframework.boot</groupId>
  20.             spring-boot-starter-web</artifactId>
  21.         </dependency>
  22.         
  23.         
  24.         <dependency>
  25.             <groupId>org.springframework.boot</groupId>
  26.             spring-boot-starter-data-redis</artifactId>
  27.         </dependency>
  28.         
  29.         
  30.         <dependency>
  31.             <groupId>org.apache.commons</groupId>
  32.             commons-pool2</artifactId>
  33.         </dependency>
  34.         
  35.         
  36.         <dependency>
  37.             <groupId>org.springframework.boot</groupId>
  38.             spring-boot-starter-test</artifactId>
  39.             <scope>test</scope>
  40.         </dependency>
  41.     </dependencies>
  42.     <build>
  43.         <plugins>
  44.             <plugin>
  45.                 <groupId>org.springframework.boot</groupId>
  46.                 spring-boot-maven-plugin</artifactId>
  47.             </plugin>
  48.         </plugins>
  49.     </build>
  50. </project>
复制代码
2.3 配置 Redis 连接(application.yml)

在 src/main/resources 下创建/修改 application.yml,配置 Redis 连接信息(和前面启动的容器对应):
  1. spring:
  2.   # Redis 配置
  3.   redis:
  4.     host: localhost  # Docker 部署的 Redis 地址,本地填 localhost
  5.     port: 6379       # 映射的端口
  6.     password: 123456 # 启动容器时设置的密码
  7.     database: 0      # 使用第 0 个数据库
  8.     # 连接池配置(关键,避免频繁创建连接)
  9.     lettuce:
  10.       pool:
  11.         max-active: 8   # 最大连接数
  12.         max-idle: 8     # 最大空闲连接
  13.         min-idle: 0     # 最小空闲连接
  14.         max-wait: 1000ms # 连接等待时间
复制代码
三、核心代码编写(RedisBloom 方案)

3.1 配置 RedisTemplate(解决序列化问题)

创建 config/RedisConfig.java,配置 RedisTemplate 确保命令执行正常:
  1. package com.example.springbootbloomfilter.config;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.data.redis.connection.RedisConnectionFactory;
  5. import org.springframework.data.redis.core.RedisTemplate;
  6. import org.springframework.data.redis.serializer.StringRedisSerializer;
  7. /**
  8. * Redis 配置类:解决序列化问题,确保 RedisTemplate 能正常执行 BF 命令
  9. */
  10. @Configuration
  11. public class RedisConfig {
  12.     @Bean
  13.     public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
  14.         RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
  15.         redisTemplate.setConnectionFactory(factory);
  16.         // 设置 key 和 value 的序列化器(String 序列化,避免乱码)
  17.         StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
  18.         redisTemplate.setKeySerializer(stringRedisSerializer);
  19.         redisTemplate.setValueSerializer(stringRedisSerializer);
  20.         redisTemplate.setHashKeySerializer(stringRedisSerializer);
  21.         redisTemplate.setHashValueSerializer(stringRedisSerializer);
  22.         redisTemplate.afterPropertiesSet();
  23.         return redisTemplate;
  24.     }
  25. }
复制代码
3.2 封装布隆过滤器工具类(核心)

创建 util/RedisBloomFilterUtil.java,封装布隆过滤器的核心操作(初始化、添加、查询),新手可直接复制使用:
  1. package com.example.springbootbloomfilter.util;
  2. import org.springframework.data.redis.core.RedisTemplate;
  3. import org.springframework.stereotype.Component;
  4. import javax.annotation.Resource;
  5. /**
  6. * Redis 布隆过滤器工具类(基于 RedisBloom 模块)
  7. * 保姆级封装:所有方法直接调用,无需关心底层细节
  8. */
  9. @Component
  10. public class RedisBloomFilterUtil {
  11.     // 注入配置好的 RedisTemplate
  12.     @Resource
  13.     private RedisTemplate<String, Object> redisTemplate;
  14.     /**
  15.      * 初始化布隆过滤器
  16.      * @param bloomKey 布隆过滤器的 key
  17.      * @param errorRate 误判率(推荐 0.01~0.001)
  18.      * @param capacity 预计存储的元素数量
  19.      * @return true=初始化成功,false=已存在(无需重复初始化)
  20.      */
  21.     public boolean initBloomFilter(String bloomKey, double errorRate, long capacity) {
  22.         try {
  23.             // 执行 BF.RESERVE 命令初始化
  24.             redisTemplate.execute((connection) -> {
  25.                 connection.execute("BF.RESERVE",
  26.                         bloomKey.getBytes(),
  27.                         String.valueOf(errorRate).getBytes(),
  28.                         String.valueOf(capacity).getBytes());
  29.                 return null;
  30.             });
  31.             System.out.println("布隆过滤器 [" + bloomKey + "] 初始化成功");
  32.             return true;
  33.         } catch (Exception e) {
  34.             // 捕获“已存在”异常,避免重复初始化报错
  35.             if (e.getMessage().contains("already exists")) {
  36.                 System.out.println("布隆过滤器 [" + bloomKey + "] 已存在,无需重复初始化");
  37.                 return false;
  38.             }
  39.             // 其他异常抛出,方便排查问题
  40.             throw new RuntimeException("初始化布隆过滤器失败:" + e.getMessage(), e);
  41.         }
  42.     }
  43.     /**
  44.      * 添加单个元素到布隆过滤器
  45.      * @param bloomKey 布隆过滤器的 key
  46.      * @param element 要添加的元素(如 URL、用户ID)
  47.      * @return true=添加成功,false=元素已存在(RedisBloom 模块特性)
  48.      */
  49.     public boolean add(String bloomKey, String element) {
  50.         try {
  51.             Long result = (Long) redisTemplate.execute((connection) -> {
  52.                 return connection.execute("BF.ADD",
  53.                         bloomKey.getBytes(),
  54.                         element.getBytes());
  55.             });
  56.             return result != null && result == 1;
  57.         } catch (Exception e) {
  58.             throw new RuntimeException("添加元素到布隆过滤器失败:" + e.getMessage(), e);
  59.         }
  60.     }
  61.     /**
  62.      * 批量添加元素到布隆过滤器
  63.      * @param bloomKey 布隆过滤器的 key
  64.      * @param elements 要添加的元素数组
  65.      * @return 成功添加的元素数量
  66.      */
  67.     public long batchAdd(String bloomKey, String[] elements) {
  68.         try {
  69.             // 转换参数为字节数组
  70.             byte[][] args = new byte[elements.length + 1][];
  71.             args[0] = bloomKey.getBytes();
  72.             for (int i = 0; i < elements.length; i++) {
  73.                 args[i + 1] = elements[i].getBytes();
  74.             }
  75.             Long[] results = (Long[]) redisTemplate.execute((connection) -> {
  76.                 return connection.execute("BF.MADD", args);
  77.             });
  78.             // 统计成功添加的数量(返回 1 表示添加成功,0 表示已存在)
  79.             long count = 0;
  80.             if (results != null) {
  81.                 for (Long result : results) {
  82.                     if (result == 1) {
  83.                         count++;
  84.                     }
  85.                 }
  86.             }
  87.             System.out.println("批量添加 " + count + " 个元素到布隆过滤器 [" + bloomKey + "]");
  88.             return count;
  89.         } catch (Exception e) {
  90.             throw new RuntimeException("批量添加元素失败:" + e.getMessage(), e);
  91.         }
  92.     }
  93.     /**
  94.      * 判断元素是否存在于布隆过滤器中
  95.      * @param bloomKey 布隆过滤器的 key
  96.      * @param element 要查询的元素
  97.      * @return true=可能存在(有误判率),false=绝对不存在
  98.      */
  99.     public boolean exists(String bloomKey, String element) {
  100.         try {
  101.             Long result = (Long) redisTemplate.execute((connection) -> {
  102.                 return connection.execute("BF.EXISTS",
  103.                         bloomKey.getBytes(),
  104.                         element.getBytes());
  105.             });
  106.             return result != null && result == 1;
  107.         } catch (Exception e) {
  108.             throw new RuntimeException("查询布隆过滤器失败:" + e.getMessage(), e);
  109.         }
  110.     }
  111. }
复制代码
四、测试验证(保姆级测试步骤)

创建 test/java/com/example/springbootbloomfilter/SpringbootBloomfilterApplicationTests.java,编写单元测试验证所有功能:
  1. package com.example.springbootbloomfilter;
  2. import com.example.springbootbloomfilter.util.RedisBloomFilterUtil;
  3. import org.junit.jupiter.api.Test;
  4. import org.springframework.boot.test.context.SpringBootTest;
  5. import javax.annotation.Resource;
  6. /**
  7. * 布隆过滤器测试类:逐一验证初始化、添加、查询功能
  8. */
  9. @SpringBootTest
  10. class SpringbootBloomfilterApplicationTests {
  11.     // 注入封装好的工具类
  12.     @Resource
  13.     private RedisBloomFilterUtil redisBloomFilterUtil;
  14.     // 定义布隆过滤器的 key(统一管理,避免写错)
  15.     private static final String BLOOM_KEY = "user_id_bloom";
  16.     @Test
  17.     void testInitBloomFilter() {
  18.         // 初始化:误判率 0.01(1%),预计存储 10000 个用户ID
  19.         redisBloomFilterUtil.initBloomFilter(BLOOM_KEY, 0.01, 10000);
  20.     }
  21.     @Test
  22.     void testAddElement() {
  23.         // 添加单个元素:用户ID 10001
  24.         boolean addResult = redisBloomFilterUtil.add(BLOOM_KEY, "10001");
  25.         System.out.println("添加用户ID 10001 结果:" + addResult); // true
  26.         // 重复添加同一个元素
  27.         boolean addResult2 = redisBloomFilterUtil.add(BLOOM_KEY, "10001");
  28.         System.out.println("重复添加用户ID 10001 结果:" + addResult2); // false(已存在)
  29.     }
  30.     @Test
  31.     void testBatchAdd() {
  32.         // 批量添加用户ID:10002、10003、10004
  33.         String[] userIds = {"10002", "10003", "10004"};
  34.         long count = redisBloomFilterUtil.batchAdd(BLOOM_KEY, userIds);
  35.         System.out.println("批量添加成功数量:" + count); // 3
  36.     }
  37.     @Test
  38.     void testExists() {
  39.         // 查询存在的元素:10001
  40.         boolean exists1 = redisBloomFilterUtil.exists(BLOOM_KEY, "10001");
  41.         System.out.println("用户ID 10001 是否存在:" + exists1); // true
  42.         // 查询不存在的元素:99999
  43.         boolean exists2 = redisBloomFilterUtil.exists(BLOOM_KEY, "99999");
  44.         System.out.println("用户ID 99999 是否存在:" + exists2); // false
  45.         // 查询批量添加的元素:10003
  46.         boolean exists3 = redisBloomFilterUtil.exists(BLOOM_KEY, "10003");
  47.         System.out.println("用户ID 10003 是否存在:" + exists3); // true
  48.     }
  49. }
复制代码
4.1 执行测试步骤(新手必看)


  • 确保 Redis 容器处于运行状态(docker ps 查看);
  • 右键点击测试类 → 选择「Run 'SpringbootBloomfilterApplicationTests'」;
  • 按顺序执行 testInitBloomFilter → testAddElement → testBatchAdd → testExists;
  • 查看控制台输出,验证所有功能是否正常。
五、备用方案:手动基于 Bitmap 实现(无 RedisBloom 模块)

如果你的 Redis 无法安装 RedisBloom 模块,可手动基于 Bitmap 实现,核心是「多哈希函数映射到位图位」:
5.1 手动布隆过滤器工具类
  1. package com.example.springbootbloomfilter.util;
  2. import org.springframework.data.redis.core.RedisTemplate;
  3. import org.springframework.stereotype.Component;
  4. import javax.annotation.Resource;
  5. import java.security.MessageDigest;
  6. import java.security.NoSuchAlgorithmException;
  7. import java.util.Arrays;
  8. import java.util.List;
  9. /**
  10. * 手动基于 Bitmap 实现的布隆过滤器(无 RedisBloom 模块时使用)
  11. */
  12. @Component
  13. public class ManualBloomFilterUtil {
  14.     @Resource
  15.     private RedisTemplate<String, Object> redisTemplate;
  16.     // 位图总位数(越大误判率越低,1000000 位 ≈ 1.2MB 内存)
  17.     private static final int BIT_SIZE = 1000000;
  18.     // 哈希函数个数(越多误判率越低,推荐 6~8 个)
  19.     private static final int HASH_NUM = 6;
  20.     /**
  21.      * 生成元素对应的多个哈希索引
  22.      */
  23.     private List<Integer> getHashIndexes(String element) {
  24.         int[] indexes = new int[HASH_NUM];
  25.         try {
  26.             MessageDigest md5 = MessageDigest.getInstance("MD5");
  27.             byte[] digest = md5.digest(element.getBytes());
  28.             // 从 MD5 摘要中生成多个哈希值
  29.             for (int i = 0; i < HASH_NUM; i++) {
  30.                 int idx = (digest[i] & 0xFF) % BIT_SIZE;
  31.                 indexes[i] = Math.abs(idx); // 避免负数索引
  32.             }
  33.         } catch (NoSuchAlgorithmException e) {
  34.             throw new RuntimeException("哈希算法异常", e);
  35.         }
  36.         return Arrays.asList(Arrays.stream(indexes).boxed().toArray(Integer[]::new));
  37.     }
  38.     /**
  39.      * 添加元素到布隆过滤器
  40.      */
  41.     public void add(String bloomKey, String element) {
  42.         List<Integer> indexes = getHashIndexes(element);
  43.         for (Integer idx : indexes) {
  44.             // 设置位图对应位为 1
  45.             redisTemplate.opsForValue().setBit(bloomKey, idx, true);
  46.         }
  47.     }
  48.     /**
  49.      * 判断元素是否存在
  50.      */
  51.     public boolean exists(String bloomKey, String element) {
  52.         List<Integer> indexes = getHashIndexes(element);
  53.         for (Integer idx : indexes) {
  54.             // 只要有一个位为 0,说明绝对不存在
  55.             if (!redisTemplate.opsForValue().getBit(bloomKey, idx)) {
  56.                 return false;
  57.             }
  58.         }
  59.         // 所有位都为 1,可能存在(有误差)
  60.         return true;
  61.     }
  62. }
复制代码
5.2 手动实现的测试方法

在测试类中添加以下方法,验证手动实现的布隆过滤器:
  1. @Resource
  2. private ManualBloomFilterUtil manualBloomFilterUtil;
  3. @Test
  4. void testManualBloomFilter() {
  5.     String manualBloomKey = "manual_user_bloom";
  6.    
  7.     // 添加元素
  8.     manualBloomFilterUtil.add(manualBloomKey, "20001");
  9.     manualBloomFilterUtil.add(manualBloomKey, "20002");
  10.    
  11.     // 查询元素
  12.     boolean exists1 = manualBloomFilterUtil.exists(manualBloomKey, "20001");
  13.     boolean exists2 = manualBloomFilterUtil.exists(manualBloomKey, "99999");
  14.     System.out.println("手动实现-20001 是否存在:" + exists1); // true
  15.     System.out.println("手动实现-99999 是否存在:" + exists2); // false
  16. }
复制代码
六、常见问题解决(保姆级避坑)


  • 报错「ERR unknown command 'BF.RESERVE'」
    → 原因:Redis 未安装 RedisBloom 模块;
    → 解决:重新用 Docker 启动包含 RedisBloom 的容器,或切换到手动 Bitmap 方案。
  • Redis 连接超时/拒绝连接
    → 检查 Redis 容器是否运行(docker start redis-bloom);
    → 检查 application.yml 中的 host/port/password 是否和容器配置一致。
  • 误判率过高
    → RedisBloom 方案:初始化时减小 errorRate(如 0.001)、增大 capacity;
    → 手动方案:增大 BIT_SIZE 或 HASH_NUM。
总结


  • 生产首选:SpringBoot + RedisBloom 模块,只需封装工具类调用 BF.RESERVE/ADD/EXISTS 命令,高效且易维护;
  • 核心步骤:部署带 RedisBloom 的 Redis → 引入 Spring Redis 依赖 → 配置 RedisTemplate → 封装工具类 → 测试验证;
  • 备用方案:无 RedisBloom 时,手动基于 Bitmap 实现,核心是「多哈希函数映射到位图位」,需手动调参控制误判率。
这份教程的代码可直接复制到你的 SpringBoot 项目中,只需修改 Redis 连接信息即可使用,新手也能快速落地。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

2026-2-7 21:50:30

举报

2026-2-8 20:10:43

举报

喜欢鼓捣这些软件,现在用得少,谢谢分享!
2026-2-11 12:54:31

举报

7 天前

举报

您需要登录后才可以回帖 登录 | 立即注册