使用SpringBoot角度讲解

布隆过滤器

布隆过滤器(Bloom Filter)是一种空间效率极高的概率型数据结构,由计算机科学家 Burton Howard Bloom 于 1970 年提出,核心用途是快速判断 “一个元素是否在某个集合中”。它的本质是通过 “牺牲部分准确性”(允许极低的假阳性),换取 “极致的空间占用和查询速度”,特别适合处理海量数据场景下的高效过滤。

优点:空间效率极高(比哈希表节省 10-100 倍内存)、查询速度快(O (k),k 为哈希函数数量);

缺点:存在误判率(可能把不存在的元素判为存在)、不支持删除元素。

核心原理

布隆过滤器的底层是二进制数组(bit array) 和k 个独立的哈希函数,核心流程分为 “插入” 和 “查询” 两步。

  1. 数据结构组成
    二进制数组(bit array):长度为m(单位:位 /bit),初始值全为 0;
    k 个哈希函数:每个函数能将任意输入(数字、字符串等)映射为[0, m-1]范围内的整数索引。
  2. 插入元素流程(以 “user1” 为例)
    ① 将元素转换为字节数组(如 “user1”→[117, 115, 101, 114, 49]);
    ② 用 k 个哈希函数计算出 k 个索引(如i1, i2, ..., ik);③ 将数组中i1, i2, ..., ik位置的 0 改为 1。

    哈希函数是已经封装了的,直接使用,传入字节数组以后,每一次计算返回二进制数组索引序号,将对应位置标记为1
    (简化示例:k=3,m=10,“user1” 的哈希索引为 2、5、7 → 数组 [0,0,1,0,0,1,0,1,0,0])
  3. 查询元素流程(判断 “user1” 是否存在)
    ① 同样将元素转为字节数组,用 k 个哈希函数计算 k 个索引;
    ② 检查数组中这些索引的位:
    若有任何一位为 0 → 元素一定不存在(100% 准确);
    若所有位都为 1 → 元素可能存在(存在误判可能)。
  4. 误判原因:哈希碰撞
    不同元素可能通过哈希函数计算出相同的 k 个索引,导致 “不存在的元素” 被误判为 “存在”。例如:“user2” 的哈希索引恰好也是 2、5、7 → 布隆过滤器会误判 “user2 存在”。
    2025-10-02T03:10:28.png

SpringBoot项目讲解

Java 21,JDK 22

导入Redisson

导入Redisson了就不用导入spring-boot-starter-data-reids了,已经包含了

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

配置文件

spring:
  application:
    name: RBloomFilterDemo
  data:
    redis:
      host: localhost
      port: 6379
      database: 2


bloom:
  filter:
    user:
      expectedInsertions: 100000  # 预期元素数量(100000)
      falseProbability: 0.0001     # 误判率(0.01%)

配置类

@Configuration
public class RedissonConfig {

    // 从配置文件读取参数
    @Value("${bloom.filter.user.expectedInsertions}")
    private long expectedInsertions;  // 预期元素数量

    @Value("${bloom.filter.user.falseProbability}")
    private double falseProbability;  // 误判率
    /**
     * 初始化用户ID布隆过滤器
     * 存储用户ID(如"user1"、"user2"等字符串)
     */
    @Bean(name="userRBloomFilter")
public RBloomFilter<String> userRBloomFilter(RedissonClient redissonClient) {
    RBloomFilter<String> bloomFilter=redissonClient.getBloomFilter("userBloomFilter_RedisKey");
    bloomFilter.tryInit(expectedInsertions,falseProbability);
    return bloomFilter;
}
}
RBloomFilter bloomFilter=redissonClient.getBloomFilter("userBloomFilter_RedisKey");是内部将布隆布过滤器需要使用到的二进制数组,增加到一共自定义key为userBloomFilter_RedisKey的键值里面,没有则创建,有则下一次直接使用
2025-10-02T03:18:35.png

实现类


@Service
public class userService {

@Autowired
private RBloomFilter<String> userBloomFilter;


public void addUser(String userId) {
    System.out.println("已经添加用户在布隆过滤器" + userId);
    userBloomFilter.add(userId);
}

public String getUser(String userId) {
    if(userBloomFilter.contains(userId)){
        return userId+"用户存在";
    }
    return userId+"用户不存在";
}
}
userBloomFilter.add(userId);内部方法,大概将userId转为字节数组,再根据字节数组使用对应哈希函数计算索引
userBloomFilter.contains(userId)判断是否存在,一定可以判断不存在的用户,但是可能会将’不存在‘的用户判断为‘存在’
,因为哈希可能冲突

测试类

@SpringBootTest
class RBloomFilterDemoApplicationTests {

    @Autowired
    private RBloomFilter<String> userRBloomFilter;

    @Autowired
    private userService userService;


    @BeforeEach
    public void initData(){
        List<String> ids = new ArrayList<>(1000);
        for (int i = 1; i <= 1000; i++) {
            ids.add("user" + i);
        }
        for (String id : ids) {
            userRBloomFilter.add(id);
        }
    }

    @Test
    void contextLoads() {
        System.out.println(userService.getUser("user100"));
        System.out.println(userService.getUser("user100000"));
        userService.addUser("user100000");
        System.out.println(userService.getUser("user100000"));

    }


}
initData预先写个方法模拟数据库返回id或者其它字段来储存在布隆过滤器
2025-10-02T03:25:41.png
下一次运行测试,这里的user100000之前被记录过了,所以这一次直接判断存在
2025-10-02T03:26:12.png