Redis-逻辑查询详情讲解

1、RedisData 实体类

复制代码
@Data
public class RedisData {
    private LocalDateTime expireTime; // 逻辑过期时间
    private Object data;              // 真实数据
}

2、逻辑过期查询

复制代码
// 线程池:用来异步重建缓存
private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);

// 锁前缀
public static final String LOCK_SHOP_KEY = "lock:shop:";

@Override
public Shop queryWithLogicalExpire(Long id) {
    String key = CACHE_SHOP_KEY + id;

    // 1. 从Redis查缓存
    String shopJson = stringRedisTemplate.opsForValue().get(key);

    // 2. 缓存不存在,直接返回null
    if (StrUtil.isBlank(shopJson)) {
        return null;
    }

    // 3. 缓存存在,反序列化
    RedisData redisData = JSONUtil.toBean(shopJson, RedisData.class);
    Shop shop = JSONUtil.toBean((JSONObject) redisData.getData(), Shop.class);
    LocalDateTime expireTime = redisData.getExpireTime();

    // 4. 判断是否过期
    if (expireTime.isAfter(LocalDateTime.now())) {
        // 未过期,直接返回
        return shop;
    }

    // 5. 已过期,尝试获取锁
    String lockKey = LOCK_SHOP_KEY + id;
    boolean isLock = tryLock(lockKey);

    // 6. 拿到锁,开独立线程重建缓存
    if (isLock) {
        CACHE_REBUILD_EXECUTOR.submit(() -> {
            try {
                this.saveShop2Redis(id, 20L); // 缓存重建
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                unlock(lockKey); // 释放锁
            }
        });
    }

    // 7. 无论有没拿到锁,都返回旧数据
    return shop;
}

3、缓存重建方法

复制代码
private void saveShop2Redis(Long id, Long expireSeconds) {
    // 1. 查询数据库
    Shop shop = getById(id);

    // 2. 封装逻辑过期对象
    RedisData redisData = new RedisData();
    redisData.setData(shop);
    redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds));

    // 3. 写入Redis
    stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY + id, JSONUtil.toJsonStr(redisData));
}

4、分布式锁

复制代码
private boolean tryLock(String key) {
    Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
    return BooleanUtil.isTrue(flag);
}

private void unlock(String key) {
    stringRedisTemplate.delete(key);
}
相关推荐
PHP实战开发录5 分钟前
PHP脚本手动能跑定时任务却失败
开发语言·php·开发
郝学胜-神的一滴1 小时前
Effective Python 条款 10 :海象运算符_=
开发语言·python·程序人生·开源
wuyk5551 小时前
Python零基础入门第十四章:异常处理(try-except)
开发语言·python
wuyk5551 小时前
【Socket 进阶之路】第 3 章 TCP 三次握手 & 四次挥手深度剖析|连接建立、断开、状态机、TIME_WAIT 核心工程问题
服务器·开发语言·网络·物联网·网络协议·tcp/ip
allnlei1 小时前
s6-overlay - 装在 Docker 容器里的轻量级管家
java·docker·容器
彧azz7 小时前
图的存储结构详解:邻接矩阵的原理、实现与应用
开发语言·数据结构·学习·php
IT_Octopus7 小时前
IntelliJ 本地日志路径自定义:`-DLOG_PATH=./logs` 为什么总“不听话“
java·log4j·intellij-idea
嵌入式学习菌7 小时前
Modbus‑RTU 数据类型分析
开发语言·单片机·bug
长征coder8 小时前
【无标题】
java·性能优化
matlab代码8 小时前
基于matlab数字图像处理的指纹识别系统【源码68期】
开发语言·matlab