拆分
User = 基础信息 + 角色 + 权限
User → 基本信息(很稳定)
Role → 角色集合(中等频率)
Permission → 权限集合(高频变动)
public Set<String> getUserPermissions(Long userId) {
// 1️⃣ 查用户最终权限缓存(L2)
Set<String> perms = redis.get("user:perms:" + userId);
if (perms != null) {
return perms;
}
// 2️⃣ 查用户角色
List<Long> roleIds = getUserRoleIds(userId);
// 3️⃣ 聚合角色权限
Set<String> result = new HashSet<>();
for (Long roleId : roleIds) {
result.addAll(getRolePermissions(roleId));
}
// 4️⃣ 写入 user:perms(短 TTL)
redis.set(
"user:perms:" + userId,
result,
5,
TimeUnit.MINUTES
);
return result;
}
每一层缓存的职责(非常重要)
| 缓存 | 负责什么 | 变更时是否需要清 |
|---|---|---|
| user:base | 用户基本信息 | 只在改用户信息时 |
| user:roles | 用户角色 | 用户角色变更 |
| role:perms | 角色权限 | 改权限时只清这个 |
| user:perms | 用户最终权限 | 删即可,自动重建 |
🔥 场景 1:修改角色的权限
// 1️⃣ 清角色权限
delete("role:perms:" + roleId);
// 2️⃣ 清所有拥有该角色的用户最终权限
delete("user:perms:" + userId); // 批量
❌ 不需要动 user:base
❌ 不需要动 user:roles
🔥 场景 2:用户换角色
delete("user:roles:" + userId);
delete("user:perms:" + userId);
🔥 场景 3:用户改用户名 / 头像
delete("user:base:" + userId);
L1 / L2 在这个模型里的最佳实践
✅ L1(本地缓存)
-
只缓存:
-
user:base
-
user:roles
-
role:perms
-
-
TTL 很短(30s~1min)
-
用 Caffeine / Guava
❌ 不建议 L1 缓存 user:perms
✅ L2(Redis)
-
所有 key 都在
-
user:perms只在 Redis(短 TTL)