当前位置: 首页 > news >正文

Redis + 布隆过滤器解决缓存穿透问题

Redis + 布隆过滤器解决缓存穿透问题

1. Redis + 布隆过滤器解决缓存穿透问题

📌 什么是缓存穿透?

缓存穿透指的是查询的数据既不在缓存,也不在数据库,导致每次查询都直接访问数据库,增加数据库压力。
例如:攻击者故意请求不存在的 ID,导致大量无效查询,冲垮数据库。

解决方案

  • 普通缓存机制:数据库查询为空时,写入一个短期过期的空值,但对高并发请求不够高效。

  • 布隆过滤器方案

    (推荐):

    1. 布隆过滤器预存已有数据的 key
    2. 查询前,先通过布隆过滤器判断 key 是否可能存在:
      • 存在 → 查询 Redis 缓存,未命中则查询数据库,再写入缓存。
      • 不存在 → 直接返回,避免访问数据库,防止缓存穿透。

2. Java 代码示例:Redis + 布隆过滤器

📌 依赖

使用 Redisson 库来操作 Redis 的布隆过滤器,需要添加以下依赖(使用 MavenGradle)。

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.21.1</version>
</dependency>

📌 代码示例

🚀 1. 初始化 Redis 连接
import org.redisson.Redisson;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

public class RedisBloomFilter {
    private static RedissonClient redissonClient;
    private static RBloomFilter<String> bloomFilter;

    static {
        // 配置 Redis 连接
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379"); // 连接本地 Redis
        redissonClient = Redisson.create(config);

        // 初始化布隆过滤器
        bloomFilter = redissonClient.getBloomFilter("product_bloom_filter");
        bloomFilter.tryInit(1000000L, 0.01); // 预估 100w 个元素,误判率 1%
    }

    /**
     * 向布隆过滤器添加数据
     */
    public static void addToBloomFilter(String productId) {
        bloomFilter.add(productId);
    }

    /**
     * 判断数据是否可能存在
     */
    public static boolean mightContain(String productId) {
        return bloomFilter.contains(productId);
    }

    public static void main(String[] args) {
        // 模拟初始化布隆过滤器
        addToBloomFilter("1001");
        addToBloomFilter("1002");
        addToBloomFilter("1003");

        // 查询数据
        System.out.println("1001 存在?" + mightContain("1001")); // true
        System.out.println("2001 存在?" + mightContain("2001")); // false
    }
}

🚀 2. 结合 Redis 缓存 + MySQL 数据库

import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import redis.clients.jedis.Jedis;

public class BloomFilterCacheDemo {
    private static Jedis redisClient = new Jedis("127.0.0.1", 6379);
    private static RedissonClient redissonClient;
    private static RBloomFilter<String> bloomFilter;

    static {
        // 配置 Redisson 连接 Redis
        redissonClient = Redisson.create();
        bloomFilter = redissonClient.getBloomFilter("product_bloom_filter");
        bloomFilter.tryInit(1000000L, 0.01); // 100w 数据,误判率 1%
    }

    /**
     * 模拟查询数据库
     */
    public static String queryDatabase(String productId) {
        if ("1001".equals(productId)) {
            return "商品1001:iPhone 15";
        } else if ("1002".equals(productId)) {
            return "商品1002:MacBook Pro";
        }
        return null; // 模拟数据库查询不到
    }

    /**
     * 查询商品详情(使用布隆过滤器+Redis缓存)
     */
    public static String getProductInfo(String productId) {
        // 1️⃣ 先查询布隆过滤器
        if (!bloomFilter.contains(productId)) {
            return "商品不存在"; // 直接返回,防止缓存穿透
        }

        // 2️⃣ 查询 Redis 缓存
        String cacheData = redisClient.get("product:" + productId);
        if (cacheData != null) {
            return "【缓存】" + cacheData;
        }

        // 3️⃣ 查询数据库
        String dbData = queryDatabase(productId);
        if (dbData != null) {
            // 写入 Redis,设置 10 分钟过期
            redisClient.setex("product:" + productId, 600, dbData);
            return "【数据库】" + dbData;
        }

        return "商品不存在";
    }

    public static void main(String[] args) {
        // 初始化布隆过滤器(模拟数据库已有商品)
        bloomFilter.add("1001");
        bloomFilter.add("1002");

        // 测试查询
        System.out.println(getProductInfo("1001")); // 【数据库】商品1001:iPhone 15
        System.out.println(getProductInfo("2001")); // 商品不存在
    }
}

3. 运行结果

查询商品 IDRedis 缓存数据库布隆过滤器返回结果
1001❌ 无✅ 有✅ 可能存在【数据库】商品1001:iPhone 15
1001(第二次查询)✅ 有-✅ 可能存在【缓存】商品1001:iPhone 15
2001(不存在)❌ 无❌ 无❌ 一定不存在商品不存在

4. 关键点总结

布隆过滤器防止缓存穿透

  • 先用布隆过滤器检查,如果 不在过滤器内,直接返回,避免查询数据库。

Redis 作为缓存

  • 查询时,优先访问 Redis,如果缓存未命中,才访问数据库,并写入 Redis 进行缓存。

误判率低

  • 通过 bloomFilter.tryInit(1000000L, 0.01),设置合理的 n(数据量)和 p(误判率),确保高效过滤。

不能删除数据

  • 布隆过滤器不支持删除,可以用定期重建的方式优化,例如每天重新初始化。

5. 适用场景

🔹 电商系统:防止查询不存在的商品 ID,减少数据库压力。
🔹 用户系统:防止查询不存在的用户 ID,避免账号碰撞攻击。
🔹 API 限流:判断 IP 或 Token 是否在黑名单,快速拒绝非法请求。

相关文章:

  • 微服务 - 中级篇
  • WebLogic中间件常见漏洞
  • sqrt函数(豆包)
  • 【leetcode100】搜索二维矩阵
  • 整合vue+Element UI 开发管理系统
  • 浅谈布隆过滤器(Bloom Filter)
  • kotlin知识体系(四) : inline、noinline、crossinline 关键字对应编译后的代码是怎样的 ?
  • 西门子G120扭矩控制
  • 系统设计类问题回答模板
  • 平芯微PW5012应用电路
  • emWin自定义键盘布局
  • 实验一、Linux环境下实现进度条小程序:深入解析核心实现与关键技术细节
  • 英伟达有哪些支持AI绘画的 工程
  • VMWare虚拟机磁盘扩容
  • Dify 0.15.3 输入变量无法被重新赋值问题-解决方法
  • 用selenium+ChromeDriver豆瓣电影 肖申克的救赎 短评爬取(pycharm 爬虫)
  • 基于CNN-LSTM联合网络的主瓣干扰辨识
  • 三个HTTP请求参数注解@RequestHeader、@RequestParam和@RequestBody的使用对比
  • 神聖的綫性代數速成例題21. 酉空間的基本概念、酉變換與酉矩陣的性質及應用、矩陣的奇異值分解及其應用
  • Eclipse Debug 调试
  • 北京朝阳涉住宅组团地块126亿元成交
  • 特朗普声称中方领导人打了电话,外交部:近期中美元首没有通话
  • 杭州6宗涉宅用地收金125.76亿元,萧山区地块楼面价冲破5万元/平米
  • 伊朗港口爆炸已致46人死亡
  • 央视曝光假进口保健品:警惕!保税仓发货不等于真进口
  • 屋顶上的阳光与火光:战争如何改变了加沙的能源格局