布隆过滤器(解决redis缓存穿透步骤之一)

布隆过滤器详解

1、是什么

布隆过滤器(Bloom Filter):一个很长的二进制位数组 + N 个哈希函数 ,用来快速判断:某个元素一定不存在 / 可能存在

✅特点:

  • 判断不存在:100% 准确
  • 判断存在 :有误判概率(可能把不在的元素判定为存在)
  • 不能删除元素(普通布隆),只能新增、查询
  • 不存储原始数据,只存 hash 映射后的 bit 位,内存占用极小

2、工作原理

  1. 初始化:长度为 m 的 bit 数组,全部置 0
  2. 添加元素 元素经过 k 个不同哈希函数,算出 k 个下标,把数组对应下标全部置为 1

同一个位置多次置 1,还是 1。

  1. 查询元素是否存在 对元素执行同样 k 个 hash,拿到 k 个下标:
  • 只要有任意一位是 0 → 元素一定不存在
  • 全部都是 1 → 元素可能存在(存在误判)

误判来源:不同元素 hash 刚好落在同一批 bit 位上。

❌不能删除:如果把某个 bit 置 0,会影响其他共用这个 bit 的元素。

如果需要删除:使用计数布隆过滤器,bit 数组改成计数器数组。


3、核心参数

  • m:bit 数组长度(越大,误判率越低,内存越大)
  • n:预期存放元素数量
  • k:哈希函数个数
  • 误判率 p:业务上可以接受的错误概率

经验公式: 最佳哈希函数数量 k = (m/n)*ln2 m 越大、n 越小,误判率越低。


4、Java/Redis 实际使用场景

场景 1:缓存穿透(最经典面试)

大量请求查询数据库不存在的数据,直接打穿 Redis 打到 DB。

方案:把所有有效 key 放入布隆过滤器。 请求过来先过布隆:

  • 返回不存在 → 直接返回,不查 redis、不查 DB
  • 返回可能存在 → 再查缓存、数据库

注意:布隆过滤器有误判,不会 100% 拦截。

场景 2:黑名单、垃圾过滤

黑名单手机号、url,判断是否在黑名单。

场景 3:去重

比如爬虫,判断 url 是否已经爬过;大数据量去重。

❗不能做:精确判断存在、需要删除元素的业务。


5、Redis 布隆过滤器

Redis4.0 以后提供插件 RedisBloom

复制代码
# 添加
BF.ADD bloom_user 10001
# 查询
BF.EXISTS bloom_user 10001

Redis 布隆也不支持删除。

实例 1:Redis 布隆过滤器解决缓存穿透(伪业务代码,Java)

前提:Redis 安装 RedisBloom 模块,命令:BF.ADDBF.EXISTS

业务:商品查询

  1. 项目启动预热:把数据库全部合法商品 id 加载进布隆过滤器
java 复制代码
// 项目启动,把所有商品id存入布隆
List<Long> allGoodsId = goodsMapper.selectAllGoodsId();
for (Long id : allGoodsId) {
    // BF.ADD bloom:goods 1001
    redisTemplate.opsForValue().execute((RedisCallback<Object>) conn -> {
        conn.bfAdd("bloom:goods", id.toString().getBytes());
        return null;
    });
}
  1. 查询商品完整流程
java 复制代码
public Goods getGoodsById(Long goodsId){
    //①第一步:布隆过滤器判断
    Boolean mayExist = (Boolean)redisTemplate.opsForValue().execute(conn->{
        return conn.bfExists("bloom:goods",goodsId.toString().getBytes());
    });
    //布隆返回false:一定不存在,直接返回,不走redis、不走DB
    if(Boolean.FALSE.equals(mayExist)){
        return null;
    }

    //②布隆说可能存在,查询Redis缓存
    String cacheJson = redisTemplate.opsForValue().get("goods:"+goodsId);
    if(cacheJson != null){
        return JSONUtil.toBean(cacheJson,Goods.class);
    }

    //③redis未命中,查询数据库
    Goods goods = goodsMapper.selectById(goodsId);
    if(goods != null){
        //写入redis缓存
        redisTemplate.opsForValue().set("goods:"+goodsId,JSONUtil.toJsonStr(goods),30, TimeUnit.MINUTES);
    }
    return goods;
}

业务注意:新增商品,需要同步调用BF.ADD把新 id 加入布隆; ⚠️删除商品,普通布隆无法删除,会产生脏数据。

实例2:Guava 布隆过滤器(Java 本地)

java 复制代码
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>32.1.3-jre</version>
</dependency>
java 复制代码
// 预期元素10万,误判率0.01
BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(StandardCharsets.UTF_8),100000,0.01);
bloomFilter.put("zhangsan");
boolean exist = bloomFilter.mightContain("zhangsan");

Guava 布隆是本地内存,多实例环境无法共享,适合单机;分布式必须用 RedisBloom。


6、高频面试考点

  1. 为什么布隆过滤器不能删除?

一个 bit 位会被多个元素共享,把 bit 置 0,会把其他元素标记成不存在。 解决:计数布隆过滤器,每个位置存计数器,删除计数器减一。

  1. 为什么会误判?

哈希冲突,不同元素映射到同一组 bit。不会漏判,只会误判。 不存在一定是真的不存在;存在不一定真的存在。

  1. 缓存穿透使用布隆过滤器有什么缺点? 1)有一定误判率; 2)元素新增后布隆要同步; 3)数据大量删除,普通布隆无法处理; 4)Redis 布隆需要安装插件;Guava 本地实例之间数据不互通。
  2. 布隆过滤器、BitMap 区别
  • BitMap:一个元素对应一个 bit,无误差,但是需要知道元素最大值,适合 id 连续场景。
  • 布隆过滤器:k 个 hash 映射多个 bit,有误差,不需要知道元素范围,适合字符串、不规则 id。

7、对比小结

表格

特性 布隆过滤器 BitMap
误差 存在误判 无误差
存储原始值 不存储 不存储
删除 普通版本不支持 支持
适用数据 字符串、离散 id 连续数字 id
内存 极小 取决于最大值
相关推荐
数安3000天18 分钟前
数据分类分级产品有哪四类技术路线?
大数据·数据库
泡茶喝茶写代码20 分钟前
量化数据开发实战系列(第 25 篇):基金基础接口实战:基金列表、ETF-LOF 分类、基金概况、净值数据
java·数据库·人工智能·python·mysql
张小姐的猫23 分钟前
【AI大模型接入SDK】 —— 数据管理 & 与Session模块进行联动
数据结构·数据库·c++·人工智能·python·chatgpt
IvorySQL1 小时前
PostgreSQL 日报|大模型破解在线校验和(9 月 15 日)
数据库·人工智能·postgresql
sdm0704271 小时前
Linux 中安装 Redis 5
redis
Leon-Ning Liu2 小时前
Oracle 19c ASMB 进程 PGA 内存泄漏排查实战 (Bug 38610635)
数据库·oracle
步行cgn2 小时前
Spring 注入 null 和空字符串详解
数据库·spring·oracle
Allen_LVyingbo2 小时前
医疗人工智能项目全生命周期管理系统:监管知识建模、工程实现与实证评估(下)
大数据·数据库·人工智能·python·自然语言处理·自动化
草莓熊Lotso2 小时前
【Redis 进阶】主从复制深度解析:从配置落地到 PSYNC 同步原理
linux·开发语言·网络·数据库·redis·缓存·php
YangYang9YangYan2 小时前
2026 校招市场数据分析 JD 拆解,SQL 要求、工具与面试考点
数据库·人工智能·数据分析