Redis缓存穿透(缓存空值,布隆过滤器)+代码实现

缓存穿透

1.缓存空对象

	/**
     * 缓存空值解决缓存穿透
     * @param id
     * @return
     */
    @Override
    public Result queryById(Long id) {
        String cacheShopKey = CACHE_SHOP_KEY + id;
        // 1.从Redis中查询id
        String shopJson = stringRedisTemplate.opsForValue().get(cacheShopKey);

        if (StrUtil.isNotBlank(shopJson)) {
            // 2.命中返回商铺信息
            return Result.ok(JSONUtil.toBean(shopJson, Shop.class));
        }else if(shopJson != null){
            // 2.1.缓存为空直接返回错误信息
            return Result.fail("店铺信息不存在!");
        }
        // 3.未命中查询数据库
        Shop shop = getById(id);
        // 4.商铺不在数据库
        if(shop == null){
            // 4.1.缓存空值
            stringRedisTemplate.opsForValue().set(cacheShopKey, "", CACHE_NULL_TTL, TimeUnit.MINUTES);
            // 4.2.返回错误信息
            return Result.fail("店铺不存在");
        }
        // 5.商铺在数据库
        // 5.1.保存到Redis中
        stringRedisTemplate.opsForValue().set(cacheShopKey, JSONUtil.toJsonStr(shop));
        stringRedisTemplate.expire(cacheShopKey, CACHE_SHOP_TTL, TimeUnit.MINUTES);
        // 5.2.返回商铺信息
        return Result.ok(shop);
    }

2.布隆过滤器

布隆过滤器(Bloom Filter)是一种空间效率高的概率数据结构,用于测试一个元素是否属于一个集合。它可以有效地表示一个集合,并快速判断某个元素是否在集合中。布隆过滤器的核心思想是通过多个哈希函数对数据进行多次映射,从而减少存储空间,并提供快速的查询操作。

工作原理:
  1. 初始化: 初始化一个大小为 m 的位数组,每个位置初始值为 0。选择 k 个独立的哈希函数,每个哈希函数将输入元素映射到 [0, m-1] 范围内的一个位置。
  2. 添加元素:
    • 对于要加入集合的每个元素,用 k 个哈希函数对该元素进行哈希运算,得到 k 个哈希值。
    • 将这 k 个哈希值对应的位数组中的位置设为 1。
  1. 查询元素:
    • 对于要查询的元素,同样使用 k 个哈希函数对该元素进行哈希运算,得到 kkk 个哈希值。
    • 检查位数组中这 k 个位置,如果其中有任何一个位置为 0,则该元素一定不在集合中。
    • 如果这 k 个位置全部为 1,则该元素可能在集合中。
布隆过滤器的优缺点:
优点:
  1. 空间效率高: 相比于直接存储所有元素,布隆过滤器使用的存储空间更少。
  2. 插入和查询速度快: 插入和查询操作的时间复杂度都是 O(k),其中 k 是哈希函数的个数。
缺点:
  1. 存在误判率: 布隆过滤器只能告诉你一个元素“可能在”集合中,或者“肯定不在”集合中。它会有一定的误判率,即可能会误认为一个不在集合中的元素在集合中。
  2. 删除操作困难: 一旦元素插入布隆过滤器后,就很难删除它们,因为无法确定某个位上的 1 是否是由多个元素共同设置的。
实现:
  1. 添加Guava依赖
    在 pom.xml 中添加 Guava 依赖:
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.0.1-jre</version>
</dependency>
  1. 定义布隆过滤器
    在你的服务类中定义和初始化布隆过滤器:
/**
 * 布隆过滤器解决缓存穿透
 * @param id
 * @return
 */
private BloomFilter bloomFilter;
@PostConstruct
public void init(){
    // 初始化布隆过滤器,假设最多有1000000个店铺,误判率为0.01
    bloomFilter = BloomFilter.create(Funnels.stringFunnel(StandardCharsets.UTF_8),
            100000, 0.1);
    // 预加载布隆过滤器
    List<Shop> shopList = list();
    for(Shop shop : shopList){
        bloomFilter.put(CACHE_SHOP_KEY + shop.getId());
    }
}
@Override
public Result queryById(Long id) {
    String cacheShopKey = CACHE_SHOP_KEY + id;
    // 1.布隆过滤器过滤,不存在直接返回错误信息
    if(!bloomFilter.mightContain(cacheShopKey)){
        return Result.fail("店铺不存在");
    }
    // 2.可能存在,从Redis中查询id
    String shopJson = stringRedisTemplate.opsForValue().get(cacheShopKey);
    if (StrUtil.isNotBlank(shopJson)) {
        // 2.1命中返回商铺信息
        return Result.ok(JSONUtil.toBean(shopJson, Shop.class));
    }
    // 3.未命中查询数据库
    Shop shop = getById(id);
    // 4.商铺不在数据库,返回错误信息
    if(shop == null){
        return Result.fail("店铺不存在");
    }
    // 5.商铺在数据库
    // 5.1.保存到Redis中
    stringRedisTemplate.opsForValue().set(cacheShopKey, JSONUtil.toJsonStr(shop));
    stringRedisTemplate.expire(cacheShopKey, CACHE_SHOP_TTL, TimeUnit.MINUTES);
    // 5.2.返回商铺信息
    return Result.ok(shop);
}

效果:

postman请求id不在数据库中的商铺时,并没有从数据库中查取,直接被过滤掉了

  • 15
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
布隆过滤器是一种非常实用的解决缓存穿透缓存击穿和缓存雪崩问题的工具。对于缓存穿透问题,布隆过滤器可以在缓存中存储空值,避免频繁查询数据库布隆过滤器的原理是通过多次哈希运算将元素映射到一个二进制数组中,如果某个位置的值为1,则表示该元素可能存在;如果为0,则表示该元素一定不存在。通过布隆过滤器,可以快速判断一个请求是否需要查询数据库,从而避免了缓存穿透的问题。\[3\] 对于缓存击穿问题,布隆过滤器可以用于限流和降级策略。通过对热点参数进行限流,可以控制请求的并发量,避免数据库被大量请求压垮。同时,对于无效的请求,可以进行服务降级,直接返回默认值或错误信息,而不是查询数据库。\[2\] 对于缓存雪崩问题,布隆过滤器可以作为一种多级缓存的解决方案之一。除了使用Redis作为缓存外,还可以使用Nginx缓存等其他缓存工具,将请求分散到不同的缓存层,从而减轻数据库的访问压力。同时,可以通过设置缓存的过期时间,避免大量缓存同时过期,导致数据库访问压力过大。\[2\] 总之,布隆过滤器是一种非常实用的工具,可以有效解决Redis缓存雪崩、缓存穿透缓存击穿问题。通过合理使用布隆过滤器,可以提高系统的性能和稳定性。 #### 引用[.reference_title] - *1* [redis缓存穿透之终极解决方案——布隆过滤器](https://blog.csdn.net/qq_40606397/article/details/114085367)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [redis缓存雪崩、击穿、穿透](https://blog.csdn.net/weixin_45414913/article/details/124901909)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值