【黑马店铺二刷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);
    }
相关推荐
Shely201722 分钟前
MySQL数据表管理
数据库·mysql
爬山算法29 分钟前
MongoDB(80)如何在MongoDB中使用多文档事务?
数据库·python·mongodb
APguantou35 分钟前
NCRE-三级数据库技术-第2章-需求分析
数据库·需求分析
寂夜了无痕1 小时前
MySQL 主从延迟全链路根因诊断与破局法则
数据库·mysql·mysql主从延迟
爱丽_1 小时前
分页为什么越翻越慢:offset 陷阱、seek 分页与索引排序优化
数据库·mysql
APguantou1 小时前
NCRE-三级数据库技术-第12章-备份与数据库恢复
数据库·sqlserver
Bat U1 小时前
MySQL数据库|表设计+新增+分组查询
数据库·mysql
麦聪聊数据2 小时前
企业数据流通与敏捷API交付实战(五):异构数据跨库联邦与零代码发布
数据库·sql·低代码·restful
Elastic 中国社区官方博客2 小时前
当 TSDS 遇到 ILM:设计不会拒绝延迟数据的时间序列数据流
大数据·运维·数据库·elasticsearch·搜索引擎·logstash
Omics Pro2 小时前
虚拟细胞:开启HIV/AIDS治疗新纪元的关键?
大数据·数据库·人工智能·深度学习·算法·机器学习·计算机视觉