SpringBoot 整合 Redis 布隆过滤器,解决缓存穿透实战

举报
JiHua 发表于 2026/10/24 00:07:22 2026/10/24
【摘要】 缓存穿透是后端开发高频问题:大量请求查询数据库不存在的数据,绕过缓存直接打满DB。布隆过滤器是一种空间高效的概率型数据结构,可以快速判断一个key一定不存在或者可能存在。本文基于SpringBoot整合Redisson自带布隆过滤器实现,提供完整可运行代码,讲解原理、测试方式以及误判问题的处理方案,对比缓存穿透几种解决方案的优劣,适合接入项目做缓存防护。

前言

在缓存架构中,缓存穿透、缓存击穿、缓存雪崩是经典三大问题。其中缓存穿透指:请求查询一个数据库里根本不存在的数据,缓存查不到,每次请求都落到数据库。如果被恶意批量请求,很容易压垮数据库。

常见解决方案:缓存空值、布隆过滤器。缓存空值简单,但如果不存在的key非常多,会占用大量Redis内存;布隆过滤器只存储特征,内存占用极低,适合海量key过滤场景。

重点:布隆过滤器不存在误报,存在误判。它只能保证:如果返回不存在,则一定不存在;返回存在,有可能不存在。

一、引入依赖

这里使用Redisson,内置封装好的布隆过滤器,不用自己手写位图逻辑。

<!-- Redisson -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.20.0</version>
</dependency>
<!-- redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

application.yml redis配置

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password:
    database: 0

二、布隆过滤器初始化工具类

import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;

@Component
public class BloomFilterUtil {

    @Resource
    private RedissonClient redissonClient;

    private RBloomFilter<Long> userBloomFilter;

    // 布隆过滤器名称
    private static final String BLOOM_USER = "bloom:user";

    @PostConstruct
    public void init() {
        userBloomFilter = redissonClient.getBloomFilter(BLOOM_USER);
        // 初始化:预期插入数量100万,误判率0.01(1%)
        boolean tryInit = userBloomFilter.tryInit(1000000L, 0.01);
        if (tryInit) {
            System.out.println("用户布隆过滤器初始化成功");
        } else {
            System.out.println("布隆过滤器已经存在,复用旧实例");
        }
    }

    // 添加元素
    public void addUserId(Long userId) {
        userBloomFilter.add(userId);
    }

    // 判断是否可能存在
    public boolean mightContain(Long userId) {
        return userBloomFilter.contains(userId);
    }
}

三、业务接口,增加布隆过滤器拦截逻辑

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;

@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private BloomFilterUtil bloomFilterUtil;

    @Resource
    private UserService userService;

    @GetMapping("/get")
    public String getUser(@RequestParam Long userId) {
        // 第一步:布隆过滤器判断
        boolean exist = bloomFilterUtil.mightContain(userId);
        if (!exist) {
            log.warn("布隆过滤器拦截,该用户id不存在:{}", userId);
            return "用户不存在";
        }
        // 布隆放行,再查缓存+数据库
        return userService.getUserInfo(userId);
    }
}

四、业务Service示意

import org.springframework.stereotype.Service;
import javax.annotation.Resource;

@Service
public class UserService {

    @Resource
    private BloomFilterUtil bloomFilterUtil;

    // 新增用户时,同步把id加入布隆过滤器
    public void addUser(Long userId) {
        // 保存DB逻辑...
        bloomFilterUtil.addUserId(userId);
    }

    public String getUserInfo(Long userId) {
        // 先查Redis缓存,缓存没有再查MySQL
        return "用户信息";
    }
}

五、核心问题与坑点

  1. 布隆过滤器不能删除元素
    传统布隆过滤器不支持删除,如果用户数据删除,直接从DB删掉,但布隆里面标记还在,会出现持续误判。解决方案:
  • 使用计数布隆过滤器(Redisson也支持);
  • 定时重建布隆过滤器,全量从数据库重新加载;
  • 业务上很少做物理删除,使用逻辑删除。
  1. 误判率设置
    误判率越低,需要的位图空间和哈希函数数量越多,CPU开销变大。常规业务0.01或者0.001足够。
  2. 预热问题
    项目刚启动时布隆是空的,所有请求都会放行。项目启动后需要异步任务从数据库全量加载已有数据到布隆过滤器。
// 启动后异步预热示例
@PostConstruct
public void loadAllData() {
    // 线程池异步,查询所有有效userId,循环add到布隆
}
  1. 不适合频繁新增海量数据并且会删除的场景

六、缓存穿透方案对比

方案 优点 缺点
缓存空值 实现最简单,无额外组件 不存在key很多时占用大量Redis内存
布隆过滤器 内存占用极小,适合海量key 有概率误判、无法删除、需要预热

七、生产优化建议

  1. 布隆过滤器单独一个Redis节点,避免和业务缓存相互影响;
  2. 做好监控:记录被布隆拦截的请求数量,观察拦截量;
  3. 布隆只是第一层防护,不能完全替代缓存+数据库校验;
  4. 如果是分布式多实例,共用同一个Redis上的布隆实例,数据统一。

结语

布隆过滤器是一道前置屏障,在高并发、海量查询场景下用来防御缓存穿透效果很好。很多同学直接照搬网上代码,忽略了它不能删除、需要预热、存在误判这些特性,上线踩坑。理解它的适用边界,搭配缓存空值兜底,才能形成稳定的穿透防护方案。本文基于Redisson实现,代码开箱即用,可以快速集成到现有SpringBoot项目。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。