设计iphone手机网站,网站推广公司大家好,微信营销软件,安徽网站优化怎么做更多SpringBoot3内容请关注我的专栏#xff1a;《SpringBoot3》 期待您的点赞#x1f44d;收藏⭐评论✍ 重学SpringBoot3-集成Redis#xff08;五#xff09;之布隆过滤器 1. 什么是布隆过滤器#xff1f;基本概念适用场景 2. 使用 Redis 实现布隆过滤器项目依赖Redis 配置… 更多SpringBoot3内容请关注我的专栏《SpringBoot3》 期待您的点赞收藏⭐评论✍ 重学SpringBoot3-集成Redis五之布隆过滤器 1. 什么是布隆过滤器基本概念适用场景 2. 使用 Redis 实现布隆过滤器项目依赖Redis 配置 3. 创建布隆过滤器服务4. 布隆过滤器的初始化5. 使用布隆过滤器进行缓存穿透防护6. 测试效果6.1. 启动项目6.2. 查询存在的商品6.3. 查询不存在的商品 7. 总结 在高并发场景下缓存是提升系统性能的重要手段。然而常规缓存机制中若遇到大量无效请求访问请求的 key 不存在于缓存或数据库就会导致 缓存穿透。为了应对这种问题布隆过滤器 和 缓存空值 是应对缓存穿透的两大主流方案布隆过滤器适用于大规模、复杂场景缓存空值适用于小规模场景。布隆过滤器Bloom Filter 能够通过哈希算法判断一个 key 是否可能存在减少无效请求对数据库的压力。
本篇博客将介绍如何使用 Spring Boot 3 和 Redis 实现布隆过滤器并结合示例代码来详细讲解布隆过滤器的原理和在 Redis 中的实现方式。
1. 什么是布隆过滤器
基本概念
布隆过滤器是一种空间效率高的 概率性数据结构用于快速判断某个元素是否在集合中。它有以下特点
内存占用小相比传统的集合结构布隆过滤器的内存使用更少。可能存在误判布隆过滤器只能确定某个元素“可能存在”或“绝对不存在”。但存在误判的概率可以通过调整参数降低。不支持删除布隆过滤器不支持删除已添加的元素删除某个元素会导致误判率增加。 适用场景
布隆过滤器在以下场景中非常适用
防止缓存穿透将不存在的 key 存储在布隆过滤器中避免大量无效请求直接查询数据库。防止重复数据在大规模数据处理中使用布隆过滤器避免重复处理相同的数据。
2. 使用 Redis 实现布隆过滤器
Redis 提供了开箱即用的布隆过滤器功能通过 Redis 的插件 RedisBloom安装过程参考Redis安装RedisBloom我们可以非常方便地使用布隆过滤器存储和管理 key。
项目依赖
首先在 Spring Boot 项目中引入相关依赖可参考之前文章。需要 Redis 的支持以及 Spring Data Redis 来实现与 Redis 的交互。 注意: Redisson 提供了对布隆过滤器的支持具体实现会利用它的 API。 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId
/dependency
dependencygroupIdorg.redisson/groupIdartifactIdredisson-spring-boot-starter/artifactIdversion3.20.0/version
/dependencyRedis 配置
在 application.yml 文件中配置 Redis 的连接信息详细请参考上一章重学SpringBoot3-集成Redis四之Redisson进行 Redisson 配置。
spring:redis:redisson:config: |singleServerConfig:address: redis://1.94.26.81:6379 # Redis 连接地址前缀为 redis://password: redis123456 # 如果 Redis 需要密码认证则填写密码timeout: 3000 # 命令执行超时时间毫秒配置类中初始化 Redisson 客户端
package com.coderjia.boot310redis.config;import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.spring.starter.RedissonProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** author CoderJia* create 2024/10/5 下午 04:53* Description**/
Configuration
public class RedissonConfig {Autowiredprivate RedissonProperties redissonProperties;Beanpublic RedissonClient redissonClient() throws Exception{Config config Config.fromYAML(redissonProperties.getConfig());Redisson.create(config);System.out.println(Redisson 已启动);return Redisson.create(config);}}3. 创建布隆过滤器服务
接下来我们需要定义一个服务来管理布隆过滤器。利用 Redisson 提供的 API可以轻松实现布隆过滤器。
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;Service
public class BloomFilterService {private static final String BLOOM_FILTER_NAME bloomFilter;Autowiredprivate RedissonClient redissonClient;// 初始化布隆过滤器public void initBloomFilter(long expectedInsertions, double falseProbability) {RBloomFilterString bloomFilter redissonClient.getBloomFilter(BLOOM_FILTER_NAME);bloomFilter.tryInit(expectedInsertions, falseProbability);}// 添加元素到布隆过滤器中public void addToBloomFilter(String key) {RBloomFilterString bloomFilter redissonClient.getBloomFilter(BLOOM_FILTER_NAME);bloomFilter.add(key);}// 检查元素是否存在于布隆过滤器中public boolean mightContain(String key) {RBloomFilterString bloomFilter redissonClient.getBloomFilter(BLOOM_FILTER_NAME);return bloomFilter.contains(key);}
}代码解释
初始化布隆过滤器initBloomFilter 方法可以根据期望插入的数量和误判率初始化布隆过滤器。布隆过滤器的大小和哈希函数数量根据这些参数自动计算。添加元素addToBloomFilter 方法向布隆过滤器中添加新的 key。判断元素是否存在mightContain 方法用来判断某个 key 是否在布隆过滤器中。
4. 布隆过滤器的初始化
通常我们会在应用启动时初始化布隆过滤器并将数据库中的所有 key 预先加入过滤器。
package com.coderjia.boot310redis.config;import com.coderjia.boot310redis.bean.Product;
import com.coderjia.boot310redis.dao.ProductMapper;
import com.coderjia.boot310redis.service.BloomFilterService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;import java.util.List;/*** author CoderJia* create 2024/10/6 下午 12:38* Description**/
Slf4j
Component
public class BloomFilterInitializer implements CommandLineRunner {Autowiredprivate BloomFilterService bloomFilterService;Autowiredprivate ProductMapper productMapper;Overridepublic void run(String... args) throws Exception {// 查询所有产品数据ListProduct all productMapper.findAll();// 初始化布隆过滤器bloomFilterService.initBloomFilter(all.size(), 0.01);// 将所有产品的ID加入布隆过滤器all.forEach(product - {bloomFilterService.addToBloomFilter(product.getId().toString());});log.info(初始化布隆过滤器完成添加产品数{}, all.size());}
}代码解释
在应用启动时通过 CommandLineRunner 初始化布隆过滤器并将数据库中的所有商品 ID 加入过滤器中。
5. 使用布隆过滤器进行缓存穿透防护
接下来我们通过一个简单的示例结合 Redis 的缓存功能和布隆过滤器展示如何防止缓存穿透。
package com.coderjia.boot310redis.service;import com.coderjia.boot310redis.bean.Product;
import com.coderjia.boot310redis.dao.ProductMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;/*** author CoderJia* create 2024/10/6 下午 12:37* Description**/
Slf4j
Service
public class ProductService {Autowiredprivate BloomFilterService bloomFilterService;Autowiredprivate ProductMapper productMapper;Cacheable(value product, key #p0)public Product getProductById(Long id) {// 使用布隆过滤器防止缓存穿透if (!bloomFilterService.mightContain(id.toString())) {throw new IllegalArgumentException(Product not found!);}log.info(准备查询产品信息id:{}, id);return productMapper.findById(id).orElseThrow(() - new IllegalArgumentException(Product not found!));}
}
代码解释
在查询商品前首先通过布隆过滤器判断 key 是否可能存在。若布隆过滤器判断 key 不存在则直接抛出异常避免查询数据库。如果布隆过滤器判断 key 可能存在接着通过缓存获取商品数据。如果缓存未命中则查询数据库。
productMapper 参考
package com.coderjia.boot310redis.dao;import com.coderjia.boot310redis.bean.Product;
import org.apache.ibatis.annotations.Mapper;import java.util.List;
import java.util.Optional;/*** author CoderJia* create 2024/3/16 下午 05:22* Description**/
Mapper
public interface ProductMapper {OptionalProduct findById(Long id);ListProduct findAll();
}Product 也很简单
package com.coderjia.boot310redis.bean;import lombok.Data;import java.io.Serial;
import java.io.Serializable;/*** author CoderJia* create 2024/10/6 下午 12:46* Description**/
Data
public class Product implements Serializable {Serialprivate static final long serialVersionUID 1L;private Long id;private String name;
}6. 测试效果
在你的业务逻辑中调用上面创建的 getProductById 方法。
package com.coderjia.boot310redis.demos.web;import com.coderjia.boot310redis.bean.Product;
import com.coderjia.boot310redis.service.LockService;
import com.coderjia.boot310redis.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;/*** author CoderJia* create 2024/10/5 下午 05:14* Description**/
Slf4j
RestController
public class LockController {Autowiredprivate ProductService productService;GetMapping(/get-product)public Product getProduct(RequestParam(id) Long id) {log.info(准备产品产品信息id:{}, id);try {Product product productService.getProductById(id);return product;}catch (Exception e) {log.error(获取产品信息异常id:{}, id, e);return null;}}
}6.1. 启动项目
可以看到布隆过滤器的初始化过程查询出所有的产品信息并添加到布隆过滤器中。 6.2. 查询存在的商品
调用 curl http://localhost:8080/get-product?id1 接口: 6.3. 查询不存在的商品
调用 curl http://localhost:8080/get-product?id101 接口产品不存在布隆过滤器器中直接报错。 7. 总结
通过结合 Spring Boot 3、Redis 和 Redisson我们可以非常方便地实现布隆过滤器来防止缓存穿透问题。在高并发场景下布隆过滤器是一种有效的工具可以降低数据库的压力提升系统性能。布隆过滤器并不是万能的在某些场景下会有少量误判但结合 Redis 的强大功能它依然是防止缓存穿透的最佳选择之一。
关键点总结
布隆过滤器通过空间换时间能够快速判断元素是否存在减少无效请求。Redisson 提供了开箱即用的布隆过滤器 API大大简化了开发工作。在结合缓存时布隆过滤器可以显著减少数据库查询提升系统性能。