【黑马店铺二刷day02】将店铺查询信息添加到Redis中的业务操作

什么是缓存

添加Redis缓存

流程:

实现业务:请求获取商铺信息

期间各个数据结构的变化:前端传给后端的是JSON类型,在controller层将JSON转换为java对象,后端通过SpringBoot自动将java对象转换为JSON传送给前端。

完整流程图:

浏览器请求

Controller

Service层

返回Java对象

SpringBoot调用Jackson

对象 → JSON

HTTP Response

浏览器收到JSON

缓存更新策略

为了实现帮助Redis节省内存,我们需要将Redis数据进行更新,如下三种方法为主流,具体根据业务需求进行选择。

数据库缓存不一致问题

问题原因:数据库数据因为更新导致了缓存数据与数据库信息不一致。

有如下三种解决方案



先操作缓存还是先操作数据库?

先操作数据库,再删除缓存

  • 先删除缓存,再操作数据库(发生错误概率高)
  • 先操作数据库,再删除缓存(发生错误概率低)

实现数据双写

主要完成业务就是对上述问题进行代码改进。

缓存击穿,雪崩,穿透

关于缓存击穿,雪崩,穿透,在前期已经具体进行过区分,详细可见下文。
从底层理解缓存问题:雪崩、击穿、穿透与一致性设计

业务流程更新图:

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

通过互斥锁等方法实现了缓存击穿问题的解决

业务流程图:

业务实现方式:写了两种方法

缓存穿透

Shop shop = queryWithPassThrough(id);

互斥锁解决缓存击穿

Shop shop = queryWithMutex(id);

来实现缓存存在的问题

基于逻辑过期方式解决缓存击穿问题

流程图:

实现逻辑:通过缓存逻辑过期时间将业务实现改进

java 复制代码
/**
     * 缓存重建线程池
     */
    public static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);

专为缓存重建开一个专门的线程池

java 复制代码
// 1、从Redis中查询店铺数据,并判断缓存是否命中
        String shopJson = stringRedisTemplate.opsForValue().get(key);
        if (StrUtil.isBlank(shopJson)) {
            // 1.1 缓存未命中,直接返回失败信息
            return Result.fail("店铺数据不存在");
        }
        // 1.2 缓存命中,将JSON字符串反序列化未对象,并判断缓存数据是否逻辑过期
        RedisData redisData = JSONUtil.toBean(shopJson, RedisData.class);
        // 这里需要先转成JSONObject再转成反序列化,否则可能无法正确映射Shop的字段
        JSONObject data = (JSONObject) redisData.getData();
        Shop shop = JSONUtil.toBean(data, Shop.class);
        LocalDateTime expireTime = redisData.getExpireTime();
        if (expireTime.isAfter(LocalDateTime.now())) {
            // 当前缓存数据未过期,直接返回
            return Result.ok(shop);
        }

        // 2、缓存数据已过期,获取互斥锁,并且重建缓存
        String lockKey = LOCK_SHOP_KEY + id;
        boolean isLock = tryLock(lockKey);
        if (isLock) {
            // 获取锁成功,开启一个子线程去重建缓存
            CACHE_REBUILD_EXECUTOR.submit(() -> {
                try {
                    this.saveShopToCache(id, 20L);
                } finally {
                    unlock(lockKey);
                }
            });
        }

        // 3、获取锁失败,再次查询缓存,判断缓存是否重建(这里双检是有必要的)
        shopJson = stringRedisTemplate.opsForValue().get(key);
        if (StrUtil.isBlank(shopJson)) {
            // 3.1 缓存未命中,直接返回失败信息
            return Result.fail("店铺数据不存在");
        }
        // 3.2 缓存命中,将JSON字符串反序列化未对象,并判断缓存数据是否逻辑过期
        redisData = JSONUtil.toBean(shopJson, RedisData.class);
        // 这里需要先转成JSONObject再转成反序列化,否则可能无法正确映射Shop的字段
        data = (JSONObject) redisData.getData();
        shop = JSONUtil.toBean(data, Shop.class);
        expireTime = redisData.getExpireTime();
        if (expireTime.isAfter(LocalDateTime.now())) {
            // 当前缓存数据未过期,直接返回
            return Result.ok(shop);
        }

        // 4、返回过期数据
        return Result.ok(shop);
    }
相关推荐
oradh2 小时前
Oracle 11g tar包方式安装数据库软件
数据库·oracle
顶点多余2 小时前
Mysql——表的约束
数据库·mysql
娇娇yyyyyy2 小时前
QT编程(7): Qt主窗口和菜单栏
数据库·qt·microsoft
Leon-Ning Liu2 小时前
Oracle 19C RAC升级到 26ai RAC
数据库·oracle
人间打气筒(Ada)2 小时前
mysql数据恢复实战
数据库·sql·mysql·xtrabackup·全量备份·增量备份·物理备份
爬山算法2 小时前
MongoDB(36)如何使用聚合进行分组?
数据库·mongodb
Natalia_Portman2 小时前
springboot整合DolphinDB
java·数据库·spring boot·后端·db
云边有个稻草人2 小时前
MySQL迁金仓:高兼容+自动化,国产化迁移低成本落地实战
数据库·mysql·国产数据库·kingbasees·金仓数据库·mysql迁移金仓