【黑马店铺二刷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);
    }
相关推荐
weelinking12 小时前
【2026】08_Claude与版本控制:Git协作技巧
数据库·人工智能·git·python·数据挖掘·交互·cloudera
黄焖鸡能干四碗16 小时前
固定资产管理系统建设方案和源码(Java源码)
大数据·数据库·人工智能·物联网·区块链
JoneBB17 小时前
ABAP Webservice连接
运维·开发语言·数据库·学习
解决问题no解决代码问题17 小时前
从乱码到脱敏导出:TiDB CSV 导出实战全指南
数据库
未若君雅裁17 小时前
MySQL高可用与扩展-主从复制读写分离分库分表
java·数据库·mysql
2401_8676239817 小时前
CSS Flex布局中如何设置子元素间距_掌握gap属性的现代用法
jvm·数据库·python
月落归舟17 小时前
一篇文章了解Redis内存淘汰机制与过期Key清理
数据库·redis·mybatis
phltxy18 小时前
Redis 事务
数据库·redis·缓存
康乾隆18 小时前
SQL Server Always On 重新添加从库步骤
数据库·sqlserver
环流_19 小时前
redis核心数据类型在java中的操作
java·数据库·redis