0


仿黑马点评-redis整合【二——商户查询缓存】——缓存穿透、缓存击穿的解决

前言
👏作者简介:我是笑霸final,一名热爱技术的在校学生。
📝个人主页:个人主页1 || 笑霸final的主页2
📕系列专栏:《项目专栏》
📧如果文章知识点有错误的地方,请指正!和大家一起学习,一起进步👀
🔥如果感觉博主的文章还不错的话,👍点赞👍 + 👀关注👀 + 🤏收藏🤏

在这里插入图片描述

商户查询缓存目录

🐉商户查询缓存 介绍

上节回顾

仿黑马点评-redis整合【邮件登陆部分】点此查看

本节梳理

解决缓存穿透、缓存击穿的问题

🐉添加redis缓存

流程
在这里插入图片描述

代码

@OverridepublicResultqueryById(Long id){//1.从redis查询缓存//String shopJson = stringRedisTemplate.opsForValue().get("cache:shop" + id);//这里的cache:shop可以写成字符串常量String shopJson = stringRedisTemplate.opsForValue().get(RedisConstants.CACHE_SHOP_KEY + id);//2.判断是否存在if(StrUtil.isNotBlank(shopJson)){//3.存在返回//3.1把json字符串转化为对象Shop shop =JSONUtil.toBean(shopJson,Shop.class);returnResult.ok(shop);}//4.不存在,根据id查数据库Shop shop =getById(id);//5.不存在,返回错误if(shop==null)returnResult.fail("没有此店铺");//6.存在写入redisString toJsonStr =JSONUtil.toJsonStr(shop);//转成json字符串
        stringRedisTemplate.opsForValue().set(RedisConstants.CACHE_SHOP_KEY + id,toJsonStr);//7.返回returnResult.ok(shop);//这返回的是对象}

🐉给店铺类型查询添加缓存(作业)

分析步骤我们还是可以拿上面的图
在这里插入图片描述
代码

@AutowiredprivateStringRedisTemplate stringRedisTemplate;@OverridepublicResultfindList(){//1.从redis查询缓存//构造keyString s = stringRedisTemplate.opsForValue().get("cache:list");//2.判断是否存在if(StrUtil.isNotBlank(s)){//3.存在返回List<ShopType> dtoList=JSONUtil.toList(s,ShopType.class);
            log.info("redis返回的数据");returnResult.ok(dtoList);}//4.不存在,根据id查数据库List<ShopType> typeList =this.query().orderByAsc("sort").list();//5.不存在,返回错误if(typeList.isEmpty()){returnResult.fail("错误");}//6.存在写入redisString s1 =JSONUtil.toJsonStr(typeList);
        stringRedisTemplate.opsForValue().set("cache:list",s1);//7.返回returnResult.ok(typeList);//这返回的是对象}

效果

  • 第一次查询在这里插入图片描述在这里插入图片描述
  • 第二次查询*

🐉缓存的更新策略

缓存的更新策略的方案在这里插入图片描述
上面加入redis缓存我自己加了超时剔除

🐉缓存存在的问题

下面代码访问的路径

http://localhost:8081/shop/1

1.缓存穿透

缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。
常见的解决方案有两种:

  • 缓存空对象 优点:实现简单,维护方便 缺点:额外的内存消耗,可能造成短期的不一致 适合命中不高,但可能被频繁更新的数据
  • 布隆过滤 优点:内存占用较少,没有多余key 缺点:实现复杂,存在误判可能 适合命中不高,但是更新不频繁的数据在这里插入图片描述
@OverridepublicResultqueryById(Long id){//1.从redis查询缓存//String shopJson = stringRedisTemplate.opsForValue().get("cache:shop" + id);//这里的cache:shop可以写成字符串常量String shopJson = stringRedisTemplate.opsForValue().get(RedisConstants.CACHE_SHOP_KEY + id);//2.判断是否存在if(StrUtil.isNotBlank(shopJson)){//3.存在返回//3.1把json字符串转化为对象Shop shop =JSONUtil.toBean(shopJson,Shop.class);returnResult.ok(shop);}if(shopJson==""){returnResult.fail("不存在此店铺");}//4.不存在,根据id查数据库Shop shop =getById(id);if(shop==null){//解决缓存穿透 存入空值//stringRedisTemplate.opsForValue().set(RedisConstants.CACHE_SHOP_KEY + id,null,2, TimeUnit.MINUTES);
            stringRedisTemplate.opsForValue().set(RedisConstants.CACHE_SHOP_KEY + id,"",2,TimeUnit.MINUTES);//这里不能用null,值为null会查数据库returnResult.fail("不存在此店铺");}//5.存在写入redisString toJsonStr =JSONUtil.toJsonStr(shop);//转成json字符串
        stringRedisTemplate.opsForValue().set(RedisConstants.CACHE_SHOP_KEY + id,toJsonStr,2,TimeUnit.MINUTES);//6.返回returnResult.ok(shop);//这返回的是对象}

2.缓存雪崩

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

  • 给不同的Key的TTL添加随机值
  • 利用Redis集群提高服务的可用性
  • 给缓存业务添加降级限流策略
  • 给业务添加多级缓存

3.缓存击穿

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

  • 互斥锁
  • 逻辑过期在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

互斥锁

互斥锁解决方法

最大的问题就是互相等待
在这里插入图片描述
相关方法
在这里插入图片描述

publicResultqueryById(Long id){//1.从redis查询缓存//String shopJson = stringRedisTemplate.opsForValue().get("cache:shop" + id);//这里的cache:shop可以写成字符串常量String shopJson = stringRedisTemplate.opsForValue().get(RedisConstants.CACHE_SHOP_KEY + id);//2.判断是否存在if(StrUtil.isNotBlank(shopJson)){//3.存在返回//3.1把json字符串转化为对象Shop shop =JSONUtil.toBean(shopJson,Shop.class);returnResult.ok(shop);}if(shopJson!=null){returnResult.fail("不存在此店铺");}//4不存在,实现缓存重建String lockKey="Key:shop:"+id;try{//4.1.获取互斥锁boolean isLock =tryLock(lockKey);//4.2.判断是否获取成功if(!isLock){//4.3失败休眠并重试Thread.sleep(50);returnqueryById(id);}//4.4.成功//成功应该再次检测redis缓存是否存在 这里不演示了Shop shop =getById(id);if(shop==null){//解决缓存穿透 存入空值//stringRedisTemplate.opsForValue().set(RedisConstants.CACHE_SHOP_KEY + id,null,2, TimeUnit.MINUTES);
                stringRedisTemplate.opsForValue().set(RedisConstants.CACHE_SHOP_KEY + id,"",2,TimeUnit.MINUTES);//这里不能用null,值为null会查数据库returnResult.fail("不存在此店铺");}//5.存在写入redisString toJsonStr =JSONUtil.toJsonStr(shop);//转成json字符串
            stringRedisTemplate.opsForValue().set(RedisConstants.CACHE_SHOP_KEY + id,toJsonStr,2,TimeUnit.MINUTES);//6.返回returnResult.ok(shop);//这返回的是对象}catch(InterruptedException e){thrownewRuntimeException(e);}finally{//释放互斥锁unlock(lockKey);}}

逻辑过期

逻辑过期解决方法

在这里插入图片描述
相关数据
在这里插入图片描述
在这里插入图片描述
线程池在这里插入图片描述

String key =RedisConstants.CACHE_SHOP_KEY + id;//1.从redis查询商铺缓存String json = stringRedisTemplate.opsForValue().get(key);//2.判断是否存在if(StrUtil.isBlank(json)){//3.不存在,直接返回returnnull;}//4.命中,先把json反序列化RedisData redisData =JSONUtil.toBean(json,RedisData.class);JSONObject data =(JSONObject) redisData.getData();Shop shop =JSONUtil.toBean(data,Shop.class);LocalDateTime expireTime = redisData.getExpireTime();//5.判断是否过期if(expireTime.isAfter(LocalDateTime.now())){//5.1未过期,直接返回returnResult.ok(shop);}//5.2已过期,需要缓存重建//6.缓存重建//6.1获取互斥锁String lockkey ="lock:shop:"+ id;boolean lock =tryLock(lockkey);//6.2判断是否获取锁成功if(lock){//注意 锁获取成功应该再次检测redis缓存是否过期(这里不演示)//6.3成功,开启独立线程,实现缓存重建//利用线程池去完成ExcutorService_CACHE_RECUTOR.submit(()->{//重建缓存try{this.saveShopToRdis(id,30L);}catch(Exception e){thrownewRuntimeException(e);}finally{//释放锁unlock(lockkey);}});}//6.4返回商铺信息returnResult.ok(shop);}

🔥如果感觉博主的文章还不错的话,👍点赞👍 + 👀关注👀 + 🤏收藏🤏

标签: redis 缓存 数据库

本文转载自: https://blog.csdn.net/weixin_52062043/article/details/127608572
版权归原作者 笑霸final 所有, 如有侵权,请联系我们删除。

“仿黑马点评-redis整合【二——商户查询缓存】——缓存穿透、缓存击穿的解决”的评论:

还没有评论