提前加载是在数据过期前主动刷新以避免缓存穿透,过期淘汰是过期后清理并重新加载;需结合缓存状态、加载锁与异步机制,仅用if判断时间戳会导致重复加载或脏数据。

理解多级缓存中的“提前加载”与“过期淘汰”逻辑
多级缓存(如本地缓存 + Redis)中,“提前加载”指在数据即将过期前主动刷新,避免集中失效导致的缓存穿透或数据库压力;“过期淘汰”则是在数据真正过期后清理旧值并触发重新加载。Java 中不能仅靠 if 判断时间戳就完成整个流程——必须结合缓存状态、加载锁、异步刷新等机制,否则会出现重复加载、脏数据或阻塞问题。
用 if 分支判断是否需要提前加载
核心是检查缓存剩余存活时间是否低于预设阈值(如 10 秒),且当前缓存值非空:
- 从 Redis 或本地缓存读取数据及元信息(如
expireAt时间戳) - 计算
remain = expireAt - System.currentTimeMillis() - 用 if (remain > 0 && remain 触发异步刷新(如提交到线程池)
- 注意:此时仍返回旧值,不阻塞业务请求
用 if 分支协同处理过期淘汰与重建
当缓存已过期(remain )时,需防止并发重建:
- if (cacheValue == null || isExpired(expireAt)) 进入加载流程
- 先尝试获取分布式锁(如 Redis SETNX)或本地锁(ConcurrentHashMap.computeIfAbsent)
- 加锁成功者执行 DB 查询 + 写入多级缓存;失败者可等待或直接回源(视业务容忍度)
- 写入时统一设置新过期时间,并预留提前加载窗口(如 TTL=300s,PRE_LOAD_THRESHOLD=15s)
避免 if 误判的几个关键细节
单纯依赖时间判断容易出错,需配合状态校验:
- 缓存值为 null 不一定代表未命中——可能是显式存了
null,需额外标记(如用Optional.empty()或包装类) - 系统时钟不同步会导致
System.currentTimeMillis()在多节点间误差,建议用 Redis 的TIME命令或统一 NTP 服务 - 不要在 if 条件里嵌套复杂方法调用(如
if (loadAndCache() != null)),会破坏语义且难以测试 - 推荐将判断逻辑封装为独立方法,例如
shouldPreload(long expireAt),提升可读与复用性
不复杂但容易忽略。关键是把 if 当作决策开关,而不是执行体——它只回答“要不要做”,具体怎么做交给后续的锁、线程、缓存写入等步骤来落实。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











