public User getUser(Long id) {
// 1️⃣ 查 L1
User user = localCache.getIfPresent(id);
if (user != null) {
return user;
}
// 2️⃣ 查 L2
user = redisTemplate.opsForValue().get("user:" + id);
if (user != null) {
localCache.put(id, user);
return user;
}
// 3️⃣ 查 DB
user = userMapper.selectById(id);
if (user != null) {
redisTemplate.opsForValue().set("user:" + id, user, 30, TimeUnit.MINUTES);
localCache.put(id, user);
}
return user;
}
L1 的核心作用:
-
减少 Redis QPS
-
降低网络 IO
-
把热点 Key挡在 JVM 里
L1 = 加速层
L2 = 事实缓存层
✅ 非常适合 L1 的场景
-
登录用户信息
-
租户信息
-
权限 / 菜单
-
字典数据
-
热点配置
❌ 不适合 L1 的场景
-
强一致性(余额、库存)
-
超大对象
-
低频访问
L1:localCache // JVM 内存(Caffeine / Guava / Map)
L2:Redis // 分布式缓存
L3:DB // 数据库
👉 缓存失效策略
权限变更 = 缓存失效,而不是缓存更新
1️⃣ 更新数据库(最终真相)
2️⃣ 删除 Redis(L2)
3️⃣ 删除本机 L1(可选 / 本地)
@Transactional
public void updateUserPermission(Long userId, List<Role> roles) {
// 1. 更新数据库
userMapper.updateUserRoles(userId, roles);
// 2. 删除 L2(所有节点都会 miss)
redisTemplate.delete("user:" + userId);
// 3. 删除本机 L1
localCache.invalidate(userId);
}
2️⃣ Redis Key 要有版本意识(进阶)
"user:v1:" + id
权限模型大改时,直接升版本,全量失效
用户权限变更时:
-
✅ 更新 DB
-
✅ 删除 Redis
-
✅ 本地 L1 要么删、要么靠 TTL 自愈
-
❌ 不要主动回填多级缓存