redis数据缓存以及穿透、击穿、雪崩

缓存

一 缓存基础

1 缓存的概念和作用

缓存就是数据交换的缓冲区(称作Cache),是存贮数据的临时地方,一般读写性能较高

2 缓存的使用

之前没有使用缓存是的模型

二 数据一致性

1 思路

  • 查询数据的时候,如果缓存未命中,则查询数据库,将数据写入缓存设置超时时间

  • 修改数据时,先修改数据库,在删除缓存。

  • 延时双删策略

2 代码实现

  • 修改更新方法,添加超时时间

@Override
    public Result queryById(Long id) {
        //1 redis中查询商户缓存
        String shopJson = (String)redisTemplate.opsForValue().get("cache.shop:" + id);
        //2 判断是否存在
        if(StrUtil.isNotBlank(shopJson)){
            //3存在直接返回
            Shop shop = JSONUtil.toBean(shopJson, Shop.class);
            return Result.ok(shop);
        }
        //4 不存在根据id去数据库查询
        Shop shop = this.getById(id);
        //5 数据库也不存在,返回错误
        if(shop==null){
            return Result.fail("店铺不存在");
        }
        //6 存在则写入redis中
       redisTemplate.opsForValue().set("cache.shop:" + id,JSONUtil.toJsonStr(shop),30, TimeUnit.MINUTES);
        //7 返回
        return Result.ok(shop);
    }

  • 修改Controller

 @PutMapping
    public Result updateShop(@RequestBody Shop shop) {
        // 写入数据库
​
        //shopService.updateById(shop);
        //return Result.ok();
        return  shopService.update(shop);
    }

修改service代码 延时双删策略

  

@Override
    public Result update(Shop shop) {
​
        Long id = shop.getId();
        if(id==null){
            return Result.fail("店铺id不存在");
        }
         // 删除缓存
        redisTemplate.delete("cache.shop:" + id);
        // 更新数据库
        updateById(shop);
        Thread.sleep(800);
        // 删除缓存
        redisTemplate.delete("cache.shop:" + id);
        return Result.ok();
    }

3 修改完代码以后,将所有的缓存删除,执行查询操作,多了超时

4 用postman执行修改方法: localhost:8081/shop

{
  "area":"大关",
  "sold":3035,
  "address":"金华路锦昌文华苑29号",
  "name":"102茶餐厅",
  "x":120.149192,
  "y":30.316078,
  "typeId":1,
  "id":1
}

执行完成以后,数据库的数据发生改变,查看redis的数据已经删除了。

三 缓存常见问题

1 缓存穿透:

客户端请求的数据,在数据库和redis中都不存在,这样缓存永远都不会生效,请求最终都到了数据库上。

解决方案:

  • 当在数据库查询的结果也不存在的时候,可以返回null值给redis,并且设置TTL

  • 布隆过滤器

布隆过滤器是一种数据结构,底层是位数组,通过将集合中的元素多次hash得到的结果保存到布隆过滤器中。主要作用就是可以快速判断一个元素是否在集合里面,但是因为算法的原因,也有一定概率的错误。

开发的时候我们一般选择空值值方式。

  • 代码方式实现

根据id查询的时候,如果信息不存在,则要将空值写入redis,并设置空值过期时间

@Override
    public Result queryById(Long id) {
        //1 redis中查询商户缓存
        String shopJson = (String)redisTemplate.opsForValue().get("cache.shop:" + id);
        //2 判断是否存在
        if(StrUtil.isNotBlank(shopJson)){
            //3存在直接返回
            Shop shop = JSONUtil.toBean(shopJson, Shop.class);
            return Result.ok(shop);
        }
        if(shopJson!=null){
            return Result.fail("店铺不存在");
        }
        //4 不存在根据id去数据库查询
        Shop shop = this.getById(id);
        //5 数据库也不存在,返回错误
        if(shop==null){
            // 空值写入redis中
            redisTemplate.opsForValue().set("cache.shop:" + id,"",3, TimeUnit.MINUTES);
            return Result.fail("店铺不存在");
        }
        //6 存在则写入redis中
       redisTemplate.opsForValue().set("cache.shop:" + id,JSONUtil.toJsonStr(shop),30, TimeUnit.MINUTES);
        //7 返回
        return Result.ok(shop);
    }

2 缓存击穿:

也叫热点key问题,一个被高并发访问且业务复杂的key突然失效了,无数的请求瞬间给数据库带来的巨大冲击

解决方案: 互斥锁 逻辑过期

互斥锁思路:

查询缓存的时候,未命中需要获取锁 代码ShopServiceImpl

@Override
public Result queryById(Long id) {
    //1 redis中查询商户缓存
    String shopJson = (String)redisTemplate.opsForValue().get("cache.shop:" + id);
    //2 判断是否存在
    if(StrUtil.isNotBlank(shopJson)){
        //3存在直接返回
        Shop shop = JSONUtil.toBean(shopJson, Shop.class);
        return Result.ok(shop);
    }
    if(shopJson!=null){
        return Result.fail("店铺不存在");
    }
    Shop shop = null;
    String lockKey = "lock.id:" + id;
    try {
        //代码到这里说明没有命中缓存,那么就可以获取锁了
        boolean isLock = tryLock(lockKey);
        // 如果没有拿到锁,则等待一会,递归执行代码
        if(!isLock){
            Thread.sleep(100);
            queryById(id);
        }
        //获取锁成功
        //4 不存在根据id去数据库查询
        shop = this.getById(id);
        //5 数据库也不存在,返回错误
        if(shop==null){
            // 空值写入redis中
            redisTemplate.opsForValue().set("cache.shop:" + id,"",3, TimeUnit.MINUTES);
            return Result.fail("店铺不存在");
        }
        //6 存在则写入redis中
        redisTemplate.opsForValue().set("cache.shop:" + id,JSONUtil.toJsonStr(shop),30, TimeUnit.MINUTES);
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        unlock(lockKey);
    }
    //7 返回
    return Result.ok(shop);
}

    // 获取锁
private boolean tryLock(String key){
    Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
    return BooleanUtil.isTrue(flag);
}
//释放锁
private void unlock(String key){
    redisTemplate.delete(key);
}

3 缓存雪崩

同一时间段内,大量的缓存key失效或者redis宕机,到时大量的请求到达数据库,带来巨大的压力。

解决方案

  • 给key设置随机的TTL

  • 集群方案防止宕机不可用

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
东南亚位于我国倡导推进的“一带一路”海陆交汇地带,作为当今全球发展最为迅速的地区之一,近年来区域内生产总值实现了显著且稳定的增长。根据东盟主要经济体公布的最新数据,印度尼西亚2023年国内生产总值(GDP)增长5.05%;越南2023年经济增长5.05%;马来西亚2023年经济增速为3.7%;泰国2023年经济增长1.9%;新加坡2023年经济增长1.1%;柬埔寨2023年经济增速预计为5.6%。 东盟国家在“一带一路”沿线国家中的总体GDP经济规模、贸易总额与国外直接投资均为最大,因此有着举足轻重的地位和作用。当前,东盟与中国已互相成为双方最大的交易伙伴。中国-东盟贸易总额已从2013年的443亿元增长至 2023年合计超逾6.4万亿元,占中国外贸总值的15.4%。在过去20余年中,东盟国家不断在全球多变的格局里面临挑战并寻求机遇。2023东盟国家主要经济体受到国内消费、国外投资、货币政策、旅游业复苏、和大宗商品出口价企稳等方面的提振,经济显现出稳步增长态势和强韧性的潜能。 本调研报告旨在深度挖掘东南亚市场的增长潜力与发展机会,分析东南亚市场竞争态势、销售模式、客户偏好、整体市场营商环境,为国内企业出海开展业务提供客观参考意见。 本文核心内容: 市场空间:全球行业市场空间、东南亚市场发展空间。 竞争态势:全球份额,东南亚市场企业份额。 销售模式:东南亚市场销售模式、本地代理商 客户情况:东南亚本地客户及偏好分析 营商环境:东南亚营商环境分析 本文纳入的企业包括国外及印尼本土企业,以及相关上下游企业等,部分名单 QYResearch是全球知名的大型咨询公司,行业涵盖各高科技行业产业链细分市场,横跨如半导体产业链(半导体设备及零部件、半导体材料、集成电路、制造、封测、分立器件、传感器、光电器件)、光伏产业链(设备、硅料/硅片、电池片、组件、辅料支架、逆变器、电站终端)、新能源汽车产业链(动力电池及材料、电驱电控、汽车半导体/电子、整车、充电桩)、通信产业链(通信系统设备、终端设备、电子元器件、射频前端、光模块、4G/5G/6G、宽带、IoT、数字经济、AI)、先进材料产业链(金属材料、高分子材料、陶瓷材料、纳米材料等)、机械制造产业链(数控机床、工程机械、电气机械、3C自动化、工业机器人、激光、工控、无人机)、食品药品、医疗器械、农业等。邮箱:market@qyresearch.com

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值