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);
}
相关推荐
jack@london3 分钟前
eclipse启动tomcat6时报错OutOfMemoryError: PermGen space
java·ide·eclipse
小江的记录本9 分钟前
【JVM虚拟机】类加载机制:类加载器、双亲委派模型、好处、破坏双亲委派的场景(附《思维导图》+《面试高频考点清单》)
java·jvm·spring boot·后端·python·spring·面试
李少兄10 分钟前
Spring 对象创建范式:依赖注入与直接实例化的边界抉择
java·后端·spring
basketball61611 分钟前
设计模式入门:2. 工厂模式详解 C++实现
开发语言·c++·设计模式
Lumbrologist11 分钟前
【C++】零基础入门 · 第 16 节:智能指针
开发语言·c++
yu859395811 分钟前
MATLAB 分支定界法(Branch and Bound)实现
开发语言·matlab
小马爱打代码16 分钟前
Spring源码中的设计模式实战:从理论到源码的深度解析
java·spring·设计模式
学会去珍惜17 分钟前
c语言编程 C语言入门 c语言(C语言程序设计教程 c语言视频教程 c语言零基础
c语言·开发语言
老码观察18 分钟前
数环通iPaaS架构设计的结构化与模块化方法论——从高内聚低耦合到工程落地的完整指南
java·服务器·网络
Devin~Y29 分钟前
智慧物流+AIGC客服Java大厂面试:Spring Boot、Kafka、Redis、JVM与RAG Agent实战
java·jvm·spring boot·redis·spring cloud·kafka·rag