可以有两种方式实现
这里选择第二种方式,第一种方式其他博主有实现,此处为第一种方式的连接。
1.pom文件引入依赖Redission
<!--布隆过滤器-->
<!-- https://mvnrepository.com/artifact/org.redisson/redisson-spring-boot-starter -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.41.0</version>
</dependency>
2.编写配置类注册RedissonClient 对象
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RedissonConfig {
@Bean
public RedissonClient redissonClient() {
// 创建配置对象
Config config = new Config();
// 配置单节点 Redis 地址
config.useSingleServer()
.setAddress("redis://192.168.75.128:6379")
.setPassword("123456")
.setDatabase(0); // 默认DB
return Redisson.create(config);
}
}
3.实现布隆过滤器类,将所有shopID放进去
import com.hmdp.mapper.ShopMapper;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
@Slf4j
@Component
public class BloomFilterInit implements CommandLineRunner {
@Resource
private RedissonClient redissonClient;
@Resource
private ShopMapper shopMapper; // 注入 ShopMapper
@Override
public void run(String... args) {
// 获取布隆过滤器实例(key 可自定义)
RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter("shop:bloom");
// 初始化:预计元素数量、误判率
bloomFilter.tryInit(1000, 0.01);
// 将所有合法 shopId 加入布隆过滤器
List<Long> shopIds = shopMapper.getAllShopIds();
for (Long id : shopIds) {bloomFilter.add(id);}
log.debug(" 布隆过滤器初始化完成,已加载 shopId 数量: "+shopIds.size());
}
}
注意啦,ShopMapper要去实现这个方法getAllShopIds()
public interface ShopMapper extends BaseMapper<Shop> {
List<Long> getAllShopIds();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.hmdp.mapper.ShopMapper">
<select id="getAllShopIds" resultType="java.lang.Long">
SELECT id FROM tb_shop
</select>
</mapper>
4.在查询时使用布隆拦截器拦截
@Override
public Result queryById(Long id) {
// 5.2 利用布隆过滤器
// 查布隆过滤器 ,当然还有问题哈,要在加入新id的时候动态更新
RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter("shop:bloom");
if (!bloomFilter.contains(id)) {
// 如果 Bloom 判断不存在,直接返回,避免击穿缓存和数据库
return Result.fail("店铺不存在(布隆过滤器拦截)");
}
// 1. 从redis查询商铺缓存
String shopJson = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY + id);
// 2. 判断是否存在
if(StrUtil.isNotBlank(shopJson)){
// 日志输出
log.info("shopJson缓存中有:{}",shopJson);
// 转换为对象
Shop shop = JSONUtil.toBean(shopJson, Shop.class);
// 3. 存在,直接返回
return Result.ok(shop);
}
// 5.1 拦截空值"" null 这个地方不能这样判断 shopJson.equals()前面必须不是null
if (shopJson!=null)
{
return Result.fail("店铺信息不存在");
}
// 4. 不存在,根据id查询数据库
Shop shop = getById(id);
// 5. 不存在,返回错误
if(shop == null){
// 缓存穿透问题
// 方案5.1. 设置 redis 该键+ 值("") 可能有短时间不一致问题
stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY+id,"",1, TimeUnit.MINUTES);//垃圾值尽可能是指更短时间
// 方案5.2. 利用布隆过滤器
return Result.fail("店铺不存在");
}
// 6. 存在,写入redis
String jsonStr = JSONUtil.toJsonStr(shop);
stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY+id, jsonStr,3L, TimeUnit.MINUTES);// 店铺在redis有效时间为3分钟
log.info("shopJson写入缓存:{}",jsonStr);
// 7. 返回
return Result.ok(shop);
}
注意,这里有个问题,要实现增加和删除是对布隆过滤器进行动态更新,可以考虑用拦截器实现。