• Redis-实战篇-缓存击穿问题及解决方案


    1、缓存击穿

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

    在这里插入图片描述

    2、常见的解决方案有两种:

    2.1、互斥锁

    在这里插入图片描述

    2.2、逻辑过期

    在这里插入图片描述

    2.3、两种方案对比

    在这里插入图片描述

    3、利用互斥锁解决缓存击穿问题

    需求:修改根据id查询商铺的业务,基于互斥锁方式来解决缓存击穿问题

    在这里插入图片描述

    3.1、ShopServiceImpl.java

    package com.hmdp.service.impl;
    
    import cn.hutool.core.util.BooleanUtil;
    import cn.hutool.core.util.StrUtil;
    import cn.hutool.json.JSONUtil;
    import com.hmdp.dto.Result;
    import com.hmdp.entity.Shop;
    import com.hmdp.mapper.ShopMapper;
    import com.hmdp.service.IShopService;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    import javax.annotation.Resource;
    import java.util.concurrent.TimeUnit;
    import static com.hmdp.utils.RedisConstants.*;
    
    /**
     * 

    * 服务实现类 *

    */
    @Service public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService { @Resource private StringRedisTemplate stringRedisTemplate; @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); } //判断命中的是否是空值 if (shopJson != null) { return Result.fail("店铺不存在!"); } //4.实现缓存重建 //4.1、获取互斥锁 String lockKey = "lock:shop:" + id; Shop shop = null; try { boolean isLock = tryLock(lockKey); //4.2、判断是否获取成功 if (!isLock) { //4.3、失败,则休眠并重试 Thread.sleep(50); return queryById(id); } //4.4、不存在,根据id查询数据库 shop = getById(id); //模拟重建延时 Thread.sleep(200); //5、数据库不存在,返回错误 if (shop == null) { // 将空值写入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); } catch (InterruptedException e) { throw new RuntimeException(e); }finally { //7、释放互斥锁 unlock(lockKey); } //8、返回 return Result.ok(shop); } private boolean tryLock(String key) { Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS); return BooleanUtil.isTrue(flag); } private void unlock(String key) { stringRedisTemplate.delete(key); } @Override @Transactional public Result update(Shop shop) { Long id = shop.getId(); if (id == null) { return Result.fail("店铺id不能为空"); } //1、更新数据库 updateById(shop); //2、删除缓存 stringRedisTemplate.delete(CACHE_SHOP_KEY + id); return Result.ok(); } }

    3.2、使用 jmeter.bat 测试高并发

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

    删除redis中的缓存,开启并发测试

    2024-06-26 19:48:17.421 DEBUG 19720 --- [io-8081-exec-46] com.hmdp.mapper.ShopMapper.selectById    : ==>  Preparing: SELECT id,name,type_id,images,area,address,x,y,avg_price,sold,comments,score,open_hours,create_time,update_time FROM tb_shop WHERE id=?
    2024-06-26 19:48:17.422 DEBUG 19720 --- [io-8081-exec-46] com.hmdp.mapper.ShopMapper.selectById    : ==> Parameters: 1(Long)
    2024-06-26 19:48:17.424 DEBUG 19720 --- [io-8081-exec-46] com.hmdp.mapper.ShopMapper.selectById    : <==      Total: 1
    
    

    我们发现只查询数据库一次,证明互斥锁方案成功,并没有所有的请求都打到数据库

    4、利用逻辑过期解决缓存击穿问题

    需求:修改根据id查询商铺的业务,基于逻辑过期方式来解决缓存击穿问题

    在这里插入图片描述

  • 相关阅读:
    食品饮料行业S2B2C系统:网站加速企业数字化转型,提高消费转化效率
    PySimpleGUI 使用浅谈
    自定义jenkins镜像提示FontConfiguration.head错误
    linux 新增定时任务
    Linux中“EXT 类型文件恢复与xfs 类型文件备份和恢复”
    【算法入门与九月打卡】—— 第五天
    从零开始搭建React+TypeScript+webpack开发环境-自定义配置化的模拟服务器
    域内持久化后门
    「技术分享」强烈推荐小白必看的Java反射
    【Linux】第五篇——Linux环境下的工具(三)(make/Makefile+进度条小程序)
  • 原文地址:https://blog.csdn.net/m0_65152767/article/details/139993018