Redis9:商户查询缓存3

欢迎来到"雪碧聊技术"CSDN博客!

在这里,您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者,还是具有一定经验的开发者,相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导,我将不断探索Java的深邃世界,分享最新的技术动态、实战经验以及项目心得。

让我们一同在Java的广阔天地中遨游,携手提升技术能力,共创美好未来!感谢您的关注与支持,期待在"雪碧聊技术"与您共同成长!

目录

一、缓存雪崩

1、什么是缓存雪崩?

2、解决方案

二、缓存击穿

1、什么是缓存击穿?

2、解决方案

①互斥锁

②逻辑过期

3、对比两种方案

4、案例:基于"互斥锁",解决缓存击穿问题

​编辑

①如何实现互斥锁呢?

②编写代码

③重启项目,使用Jmeter工具测试该后端接口

5、案例:基于"逻辑过期",解决缓存击穿问题


一、缓存雪崩

1、什么是缓存雪崩?

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

2、解决方案

二、缓存击穿

1、什么是缓存击穿?

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

举例:热点key的TTL突然到期了,线程1从redis中没命中,然后去数据库查询,并将数据重新写会redis(由于该热点key的缓存重建业务较复杂,因此重建时间比较久),在重建期间,由于该热点key是高并发的,因此此时无数线程请求该key,redis没命中,于是这些请求都打到数据库,导致数据库压力巨大。

2、解决方案

①互斥锁

核心思想:线程1发现redis不存在该热点key,于是获取互斥锁,然后查询数据库并重建该缓存,等把该热点key的缓存成功写到了redis中,才会释放这个互斥锁。这期间,其他线程,查询redis也没命中,于是也尝试获取互斥锁,但是获取失败了(因为互斥锁被线程1占用),于是无法进行查询数据库并重建缓存的动作,于是只能等待。等到线程1重建好缓存,才能命中。

优点:缓存和数据库,是数据一致的。

缺点:其他线程需要等待,导致性能不佳(服务可用性差)。

②逻辑过期

核心思想:往redis存数据时,我就不设置TTL,于是该热点key永远不会过期,因此不可能出现热点key失效的现象。

但是为了保证缓存、数据库的数据一致性,因此还是要往热点key的value当中添加一个逻辑过期时间,每次访问该热点缓存时,都要主动判断是否过期,如果过期,就获取互斥锁,然后开启一个新线程(等于叫来一个帮手),帮我去完成缓存重建的动作,我自己这个线程还是返回旧的缓存,这样的确有点数据不一致,但是也无伤大雅。

优点:无需等待,性能好(服务可用性好)。

缺点:导致轻微的数据不一致问题。

3、对比两种方案

综上:这两种方式的数据一致性、服务可用性(性能)不可兼得。

一致性和可用性的抉择,也是分布式系统里面,常常面临的一个问题。

这两种,也没有孰优孰劣,应当根据应用场景来进行抉择。

4、案例:基于"互斥锁",解决缓存击穿问题

①如何实现互斥锁呢?

我们此处借助redis的机制,来实现互斥锁。

也就是,我们获取互斥锁时,就执行setnx lock 1,即:向redis中,添加一个key为lock,value为1的键值对;释放互斥锁时,就执行del lock,即:将key为lock的数据删除。

②编写代码

先编写获取、释放互斥锁的方法。

注意:redis中的setnx命令,对应stringRedisTemplate中的setIfAbsent方法。

java 复制代码
    //自定义方法:获取互斥锁
    private boolean tryLock(String key){
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
        return BooleanUtil.isTrue(flag);//防止Boolean自动拆箱成boolean时出现空指针异常
    }
    //自定义方法:释放互斥锁
    private void unlock(String key){
        stringRedisTemplate.delete(key);
    }

编写"互斥锁"解决缓存击穿的代码(带*号的,是本次要学的)

java 复制代码
    @Override
    public Result queryById(Long id) {
        String key = CACHE_SHOP_KEY + id;
        //1、从Redis中查询商铺缓存
        String shopJson = stringRedisTemplate.opsForValue().get(key);
        //2、判断redis是否存在
        if(StrUtil.isNotBlank(shopJson)){
            //3、存在,则直接返回
            Shop shop = JSONUtil.toBean(shopJson, Shop.class);
            return Result.ok(shop);
        }
        //判断命中的是否是空值
        if(shopJson != null && shopJson.equals("")){
            //返回一个错误信息
            return Result.fail("店铺信息不存在!防止缓存穿透!");
        }

        //4、redis中不存在,进行缓存重建 *
        //4.1获取互斥锁 *
        String lockKey = "lock:shop:"+id;
        Shop shop = null;
        try {
            boolean isLock = tryLock(lockKey);
            //4.2判断是否获取成功 *
            if(!isLock){
                //4.3失败,则休眠并重试查询redis *
                Thread.sleep(50);
                return queryById(id);//递归本方法,表示重新查询redis
            }
            //4.4成功,根据id查询数据库 *
            shop = getById(id);//该方法,来自mybatisPlus
            //模拟重建缓存的延时 *
            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);
        }
        if(shop==null){ //*
            return Result.fail("店铺不存在!");
        }
        //8、返回
        return Result.ok(shop);
    }

③重启项目,使用Jmeter工具测试该后端接口

可见此时没毛病,使用"互斥锁"的方式,成功解决了"缓存击穿"的问题。

5、案例:基于"逻辑过期",解决缓存击穿问题

相关推荐
SelectDB技术团队6 小时前
快手基于 Apache Doris 千亿多模态检索的实践
数据库·数据分析·全文检索·快手·apache doris·向量索引·多模态检索
政企项目老覃6 小时前
金融转账“幽灵失败“排查实录:从本地消息表到 Seata TCC 的选型与权衡
数据库·程序人生·性能优化·数据分析·系统架构
TLA技术6 小时前
LogMiner vs 裸日志解析(三):Oracle日志解析中的“前镜像”和“后镜像”,到底怎么用?
数据库·oracle·flink·dba·迁移学习
IT邦德6 小时前
BIC-QA如何重新定义数据库知识服务
运维·数据库
逐米时代6 小时前
BOM智能构建:全链路一致性自动校验
大数据·数据库·人工智能
SelectDB技术团队6 小时前
StarRocks 迁移至 Apache Doris 完整指南:三步完成结构、数据与业务平滑切换
数据库·人工智能·sql·clickhouse·apache doris·selectdb·湖仓架构升级
Gauss松鼠会7 小时前
GaussDB 系统表与系统视图详解(内网运维版)
运维·服务器·网络·数据库·gaussdb·经验总结
倔强的石头_7 小时前
OceanBaseVS金仓:同一条复杂 SQL,为什么架构选择会改变延迟曲线
数据库
+VX:Fegn08957 小时前
计算机毕业设计|基于springboot + vue图书借阅管理系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
李金虎_7 小时前
大模型搜索时代的 GEO 工程实践:从零搭建仿真环境到可控变量的完整复盘
数据库·机器学习