【黑马店铺二刷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);
    }
相关推荐
用户30745969820717 小时前
Redis 延时队列详解
redis
GBASE17 小时前
G术时刻 |GBase 8s数据库事务并发控制之封锁技术介绍(下)
数据库
烤代码的吐司君20 小时前
Redis 数据结构 ZSet, BIT, HyperLogLog,Geo 空间数据
redis·后端
xiezhr1 天前
逛GitHub发现了一款免费的带AI功能的数据库管理工具
数据库·ai编程·dba
吃糖的小孩2 天前
给 QQ AI 机器人设计“可控记忆”:会话摘要、手动长期记忆与角色卡边界
数据库
笃行3503 天前
金仓数据库数据安全双防线:静态存储加密与传输加密实战
数据库
笃行3503 天前
金仓数据库物理备份实战:sys_rman 全流程演练与误覆盖抢救
数据库
笃行3503 天前
金仓数据库逻辑备份实战:从全库导出到 Schema 替换的完整闭环
数据库
leeyi3 天前
Checkpoint 机制:Agent 怎么在断电后接着跑
redis·aigc·agent