本文目录导读:

设计统一的 Java 缓存键名结构,核心目标是 可读性、可管理性、避免冲突、以及方便失效,没有绝对的“最佳”,但有公认的“最佳实践”。
下面为你提供一套层次分明、经过大量生产环境验证的统一键名结构方案。
核心原则
- 分层命名:像包名一样,从大到小排列,用分隔符分开。
- 可读性强:看到键名就能大致知道它缓存了什么。
- 唯一性:不同业务、不同参数组合的键不会碰撞。
- 支持通配符:方便批量失效(这是为什么结构比无规则拼接重要的原因)。
- 避免过长:在可读性和性能之间取得平衡(Redis 等键越短,查询效率越高,但可读性优先)。
推荐方案:业务域:实体名:操作:唯一标识[:属性]
这是一个经典的冒号分隔的树形结构。
格式:
[系统]:[模块]:[业务域]:[实体名]:[操作]:[唯一标识]
详细解析
- 系统 (可选):多系统共用缓存时区分。
- 模块:粗粒度分组,
user,order,product。 - 业务域:进一步细分,
user模块下的account,profile,permission。 - 实体名:具体的数据对象,
User,OrderItem,ProductSku。 - 操作:缓存是为了什么操作生成的,
detail,list,count。 - 唯一标识:通常是数据库主键 ID,或复杂的组合键(用下划线或进一步冒号连接)。
- 属性 (可选):需要缓存实体的某个具体字段时使用。
例子
| 业务场景 | 缓存键 | 解释 |
|---|---|---|
| 用户详情 | user:account:User:detail:123 |
用户模块,账户域,User实体,详情,ID=123 |
| 用户订单列表 | user:order:Order:list:456 |
用户模块,订单域,Order实体,列表,用户ID=456 |
| 商品SKU详情(带租户) | product:goods:ProductSku:detail:SKU001 |
商品模块,商品域,SKU实体,详情,SKU编码 |
| 用户权限列表 | user:permission:Permission:list:123 |
用户模块,权限域,Permission实体,列表,用户ID=123 |
| 过期时间刷新 | user:session:Session:token:a1b2c3 |
用户模块,会话域,Session实体,token值 |
| 热点文章标签 | content:article:ArticleTag:list:789 |
内容模块,文章域,ArticleTag实体,列表,文章ID=789 |
| 用户最近登录时间 | user:account:User:attr:lastLoginTime:123 |
用户模块,账户域,User实体,属性(最近登录时间),用户ID=123 |
如何用代码实现(Java + Spring Cache)
使用 RedisCacheManager 时,可以自定义 CacheKeyPrefix 或通过 @Cacheable 的 key 属性生成。
定义常量 + 工具类(推荐)
public final class CacheKeyBuilder {
private static final String SEPARATOR = ":";
// 1. 基础方法
public static String build(String... parts) {
return String.join(SEPARATOR, parts);
}
// 2. 针对常见场景的封装方法
public static String userDetail(Long userId) {
return build("user", "account", "User", "detail", String.valueOf(userId));
}
public static String userOrderList(Long userId) {
return build("user", "order", "Order", "list", String.valueOf(userId));
}
public static String productSkuDetail(String skuCode) {
return build("product", "goods", "ProductSku", "detail", skuCode);
}
// 3. 针对组合键的场景
public static String buildCompositeKey(Object... keys) {
// 用下划线连接多个标识,作为唯一标识的一部分
// user:profile:User:detail:123_456 (如果ID是组合主键)
return Arrays.stream(keys)
.map(String::valueOf)
.collect(Collectors.joining("_"));
}
}
使用示例:
@Service
public class UserServiceImpl implements UserService {
@Override
@Cacheable(value = "user_detail_cache", key = "#userId")
public User getUserById(Long userId) {
// ... 查询数据库
}
// 在Service中使用KeyBuilder手动生成key,更灵活
@Override
@Cacheable(value = "user_detail_cache",
key = "T(com.example.utils.CacheKeyBuilder).userDetail(#userId)")
public User getCachedUser(Long userId) {
// ...
}
}
使用 Spring Expression (SpEL) 在注解中直接构造
适用于简单场景,但复杂时影响可读性。
@Override
@Cacheable(value = "user_detail_cache",
key = "'user:account:User:detail:' + #userId")
public User getUserById(Long userId) {
// ...
}
// 更复杂的组合
@Override
@Cacheable(value = "order_detail_cache",
key = "'order:order:Order:detail:' + #orderId")
public Order getOrderById(String orderId) {
// ...
}
特殊场景处理
带租户的数据 (SaaS)
格式: [系统]:[租户ID]:[模块]:[实体]:[操作]:[ID]
// tenantId = 1001 // user:1001:account:User:detail:123
带版本号的数据(用于缓存刷新或平滑迁移)
格式: v[版本号]:[业务域]:[实体]:[ID]
// v2:user:account:User:detail:123 // 当数据结构变更时,只需要修改版本号,旧缓存自动失效
分页/列表查询 (谨慎使用!)
列表查询的键通常包含查询参数,这会导致缓存键爆炸,不可控。
- 不推荐:
product:goods:Product:list:page=1&size=10&sort=price - 推荐方案:
- 对列表查询使用本地内存缓存(Caffeine),并设置极短的 TTL(如 1 秒)。
- 如果非要缓存,使用 Bloom Filter 先判断,然后回源 DB。
- 如果非要缓存列表结果,将键设计为统计维度,而不是用户参数维度:
product:goods:Product:list:hot_100(缓存热门前100的商品,而非用户任意搜索参数)。
缓存键前缀(Namespace)
在 Redis 或 Caffeine 中,可以在配置时统一添加一个系统级别的前缀,防止不同应用或环境冲突。
格式: [应用名称]:[环境]:[自定义键]
// 配置类
@Bean
public StringRedisTemplate redisTemplate(RedisConnectionFactory factory) {
StringRedisTemplate template = new StringRedisTemplate(factory);
// 全局键前缀(可选)
// 实际存储:myapp:prod:user:account:User:detail:123
template.setKeySerializer(new StringRedisSerializer() {
@Override
public String serialize(String key) {
return "myapp:prod:" + key;
}
});
return template;
}
| 场景 | 推荐键结构 |
|---|---|
| 单实体详情 | user:account:User:detail:{id} |
| 列表(热点数据) | product:goods:Product:list:hot_100 |
| 用户维度列表 | user:order:Order:list:{userId} (注意缓存失效和穿透问题) |
| 组合主键 | order:item:OrderItem:detail:{orderId}_{itemId} |
| 租户隔离 | user:{tenantId}:account:User:detail:{id} |
| 需要版本管理 | v2:user:account:User:detail:{id} |
最终建议:
- 全员对齐:整个团队或项目组统一使用一套规则,建立文档。
- 避免复杂:键的可读性 > 性能微优化,不建议使用 MD5 或 64位数字。
- 列表缓存慎用:列表查询(尤其是分页、排序、搜索)尽量不加缓存,或使用本地缓存 + 极短 TTL。
- 利用冒号: 在 Redis 中并不是特殊字符,但在
keys *扫描时可以配合pattern实现批量模糊删除(生产环境谨慎使用keys,可以考虑使用SCAN或redis-cli的--pattern)。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播