基于redis缓存查询店铺

基于redis缓存查询店铺

 @Override
    public Result queryById(Long id) {
        //从redis中查询商铺缓存
        String shopJson = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY + id);
        //判断缓存是否命中
        if (StrUtil.isNotBlank(shopJson)){
            //命中,则返回店铺信息
            Shop shop = JSONUtil.toBean(shopJson, Shop.class);
            return Result.ok(shop);
        }
        //未命中,根据id查询数据库
        Shop shop = getById(id);
        //不存在该店铺,报错
        if (shop==null){
            return Result.fail("店铺不存在!");
        }
        //存在,写入redis
        stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY+id,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);
        //返回
        return Result.ok(shop);
    }

缓存更新策略

3.1 先删缓存,再操作数据库

如下图:在多线程的情况下,可能出现的问题。

如果线程1要更新数据库:先删了缓存,这是线程2想要读取数据,首先会查询缓存是否命中,这时就不能命中,然后就会去查询数据库,然后再写缓存,线程2执行完了线程1才去更新数据库,这时候就会导致缓存中的数据是旧数据。

可能性:明显更新数据库的操作更加耗费时间,所以如果需要更新数据库的时候是先删缓存再更新的话,发生缓存和数据库数据不一致的可能性还是很高的。

 3.2 先更新数据库再删缓存

如下图:线程一先读取数据,并且此时缓存没有命中(可能是缓存已经超过了失效时间了),然后线程2来了,执行了更新数据库和删缓存,然后线程一才写缓存(写入的是线程1开始从数据库中查到的旧数据),这时也会导致数据的不一致。

可能性:线程1中读取和写缓存的耗费时间没有线程二多,更新操作明显更耗时,所以这种要发生的可能性不大。

扫描二维码关注公众号,回复: 14733086 查看本文章

所以先更新数据库再删缓存是更好的选择。

 回顾:数据库操作和redis缓存中的事务回滚问题_羡云不羡君的博客-CSDN博客

缓存失效了怎么办?

https://blog.csdn.net/weixin_54401017/article/details/129711673

 缓存穿透

   缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库(一些别有用心的人就会利用这一点不断的发出这样的请求,导致服务器压力变大)。

常见的两种解决方案:

缓存空对象

  *优点:实现简单,维护方便

  *缺点:1.额外的内存消耗. 2.可能存在数据短期的不一致。

 布隆过滤

  优点:内存占用较少。没有多余的key。

  缺点:1.实现复杂 2.存在误判的可能。

布隆过滤器可以简单的理解为:是一个bite数组,里面存的是二进制位,不是把数据库里面的数据存储到布隆过滤器里面,而是把这些数据基于某种Hash算法计算出Hash值,然后再将这些Hash值转换成二进位保存到布隆过滤器里面,当我们要去判断数据库里面的数据是否存在的时候,其实就是判断对应的位置是0还是1.

基于redis缓存查询店铺考虑缓存穿透问题

@Override
    public Result queryById(Long id) {
        //从redis中查询商铺缓存
        String shopJson = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY + id);
        //判断缓存是否命中
        if (StrUtil.isNotBlank(shopJson)){
            //命中,则返回店铺信息
            Shop shop = JSONUtil.toBean(shopJson, Shop.class);
            return Result.ok(shop);
        }
        //命中为"",说明在数据库中店铺不存在
        if (shopJson !=null){
            return Result.fail("店铺不存在!");
        }
        //未命中,根据id查询数据库
        Shop shop = getById(id);
        //不存在该店铺,报错
        if (shop==null){
            //将空值写入redis中
            stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY +id,"",CACHE_NULL_TTL,TimeUnit.MINUTES);
            return Result.fail("店铺不存在!");
        }
        //存在,写入redis
        stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY+id,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);
        //返回
        return Result.ok(shop);
    }

 缓存雪崩

  缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大的压力。

 解决方案

1.由于大量的缓存同时失效,造成原因可能就是TTL时间到了。所以:给不同的Key的TTL添加随机值。

2.针对redis宕机:搭建redis集群形成主从,假设主宕机了,我们就可以从 从机替代,这样就可以确保redis可以一直对外提供服务。

3.给缓存业务添加降级限流策略

4.给业务添加多级缓存

缓存击穿

  缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。

  假设:一个热点key失效了,当线程1来了,发现没有缓存,就会去重建缓存,这个过程又比较久,在重建的过程中仍然有许多的请求。

常见的两种解决方案:

  1.互斥锁

  

缺点:性能比较差。因为在构建缓存的过程中,其他的线程都要等待。

2.逻辑过期 

 也就是在缓存中多了一个逻辑过期时间。而没有真的去设置TTL过期时间。

猜你喜欢

转载自blog.csdn.net/weixin_54401017/article/details/128426717