栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

redis解决商户查询缓存以及如何解决缓存穿透、缓存雪崩、缓存击穿

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

redis解决商户查询缓存以及如何解决缓存穿透、缓存雪崩、缓存击穿

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录
  • 什么是缓存
  • 添加Redis缓存
  • 缓存更新策略
  • 缓存穿透
  • 缓存雪崩
  • 缓存击穿
    • 互斥锁解决缓存击穿
    • 逻辑过期解决缓存击穿


什么是缓存

添加Redis缓存

    @GetMapping("/{id}")
    public Result queryShopById(@PathVariable("id") Long id) {


        return Result.ok(shopService.queryById(id));
    }
@Override
    public Result queryById(Long id) {
        String key = CACHE_SHOP_KEY+id;
        //1、从redis中查询商铺缓存
        String shopJson = stringRedisTemplate.opsForValue().get(key);

        //2、判断是否存在
        if (StrUtil.isNotBlank(shopJson)){
            //3、存在,直接返回
            Shop shop = JSONUtil.toBean(shopJson, Shop.class);
            return Result.ok(shop);
        }

        //4、不存在,根据id查询数据库
        Shop shop = getById(id);
        //5、数据库中不存在,返回错误
        if (shop==null){
            return Result.fail("店铺不存在");
        }
        //6、数据库中存在,写进redis并返回
        stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop));
        return Result.ok(shop);
    }

缓存更新策略




先删除缓存,再更新数据库

不考虑并发的情况下,不会出现缓存和数据库不一致的问题
并发情况下,就会存在缓存和数据不一致的问题

先操作数据库,再删缓存

无论那种方式,都会存在缓存和数据库不一致的问题

解决缓存于数据库部不一致的问题https://juejin.cn/post/6844903929281511438

    @Override
    @Transactional
    public Result updateShop(Shop shop) {
        Long id = shop.getId();
        if (id==null){
            return Result.fail("店铺id不能为空");
        }
        //1、更新数据库
        updateById(shop);
        //2、删除缓存
        stringRedisTemplate.delete(CACHE_SHOP_KEY+shop.getId());
        return Result.ok();

    }

@Override
    public Result queryById(Long id) {
        String key = CACHE_SHOP_KEY+id;
        //1、从redis中查询商铺缓存
        String shopJson = stringRedisTemplate.opsForValue().get(key);

        //2、判断是否存在
        if (StrUtil.isNotBlank(shopJson)){
            //3、存在,直接返回
            Shop shop = JSONUtil.toBean(shopJson, Shop.class);
            return Result.ok(shop);
        }

        //4、不存在,根据id查询数据库
        Shop shop = getById(id);
        //5、数据库中不存在,返回错误
        if (shop==null){
            return Result.fail("店铺不存在");
        }
        //6、数据库中存在,写进redis并返回
        stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);
        return Result.ok(shop);
    }

上面的解决方式是,给redis中的key都设置上时间作为兜底方案,更新数据的时候,先更新数据库再删除缓存,这种方案并不能带来强一致性

缓存穿透


缓存空对象解决缓存击穿

@Override
    public Result queryById(Long id) {
        String key = CACHE_SHOP_KEY+id;
        //1、从redis中查询商铺缓存
        String shopJson = stringRedisTemplate.opsForValue().get(key);

        //2、判断是否存在
        if (StrUtil.isNotBlank(shopJson)){
            //3、存在,直接返回
            Shop shop = JSONUtil.toBean(shopJson, Shop.class);
            return Result.ok(shop);
        }
        //todo 判断命中的是否是空值
        if (shopJson==null){
            return Result.fail("店铺信息不存在");
        }
        //4、不存在,根据id查询数据库
        Shop shop = getById(id);
        //5、数据库中不存在,返回错误
        if (shop==null){
            //todo 将控制,写入redis
            stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL,TimeUnit.MINUTES);
            return Result.fail("店铺不存在");
        }
        //6、数据库中存在,写进redis并返回
        stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);
        return Result.ok(shop);
    }

缓存雪崩

缓存击穿



互斥锁适合于解决强一致性的场景,对缓存和数据库一致性要求比较高的;如果对缓存和数据库一致性要求不高,而追求高可用的场景,逻辑过期更适合

互斥锁解决缓存击穿

public Shop queryWithMutex(Long id){
        String key = CACHE_SHOP_KEY+id;
        //1、从redis中查询商铺缓存
        String shopJson = stringRedisTemplate.opsForValue().get(key);

        //2、判断是否存在
        if (StrUtil.isNotBlank(shopJson)){
            //3、存在,直接返回
            Shop shop = JSONUtil.toBean(shopJson, Shop.class);
            return shop;
        }
        //todo 判断命中的是否是空值
        if (shopJson==null){
            return null;
        }
        //4、实现缓存重建
        //4.1、获取互斥锁
        String lockKey = "locl:shop"+id;
        boolean isLock = tryLock(lockKey);
        //4.2、判断是否获取成功
        //4.3、失败,休眠并重试
        Shop shop=null;
        try {
            if (!isLock){
                Thread.sleep(3000);
                return   queryWithMutex(id);

            }
            //4.4、成功,根据id查询数据库
             shop = getById(id);
            //5、数据库中不存在,返回错误
            if (shop==null){
                //todo 将控制,写入redis
                stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL,TimeUnit.MINUTES);
                return null;
            }
            //6、数据库中存在,写进redis并返回
            stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);

        }catch (Exception e){
            throw  new RuntimeException(e);
        }finally {
            //7、释放互斥锁
            unLocak(lockKey);
        }
        return shop;
    }

逻辑过期解决缓存击穿

  public Shop queryWithLogcialExpire(Long id){
        String key = CACHE_SHOP_KEY+id;
        //1、从redis中查询商铺缓存
        String shopJson = stringRedisTemplate.opsForValue().get(key);

        //2、判断是否存在
        if (StrUtil.isBlank(shopJson)){
            return null;
        }
        // todo 命中
        //4、需要先把json反序列化为对象
        RedisData redisData = JSONUtil.toBean(shopJson, RedisData.class);
        Shop shop = JSONUtil.toBean((JSONObject) redisData.getData(), Shop.class);

        //5、判断是否过期
        LocalDateTime expireTime = redisData.getExpireTime();
        if (expireTime.isAfter(LocalDateTime.now())){
            //5.1 未过期,直接返回店铺信息
            return shop;
        }

        //5.2  已过期,需要缓存重建
        //6、缓存重建
        //6.1、获取互斥锁
        String lockKey = LOCK_SHOP_KEY+id;
        //6.2 判断是否获取锁成功
        boolean islock = tryLock(lockKey);
        if (islock){
            // 6.3 成功,开启独立线程,实现缓存重建、
            CACHE_REBUILD_EXECUTOR.submit(()->{
                try{
                    saveShop2redis(id,30L);
                    
                }catch (Exception e){
                    throw new RuntimeException(e);
                }finally {
                    //释放锁
                    unLocak(lockKey);
                }
               
            });
        }
        // 6.4、失败,返回过期的商铺信息
       
        return shop;
    }
  private  void saveShop2redis(Long id,Long expireSeconds) throws InterruptedException {
        //查询店铺数据
        Shop shop = getById(id);
        Thread.sleep(200);
        //封装逻辑过期时间
        RedisData redisData = new RedisData();
        redisData.setData(shop);
        redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds));
        //写入redis
        stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY+id,JSONUtil.toJsonStr(redisData));
    }
@Data
public class RedisData {
    private LocalDateTime expireTime;
    private Object data;
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/956018.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号