CACHE_USAGE.md
8.55 KB
缓存功能使用指南
概述
本项目已集成了完整的多级缓存功能,基于client-api项目的缓存实现。缓存层采用 Caffeine(本地缓存)+ Redis(分布式缓存)的多级架构,通过AOP实现非侵入式的缓存管理。
特性
- 多级缓存:本地缓存(L1)+ 分布式缓存(L2)
- 非侵入式:通过注解实现,无需修改现有业务代码
- 高性能:优化的缓存穿透防护和性能优化
- 易管理:提供缓存监控和管理接口
- 支持Lombok @Data:专门优化支持Lombok @Data类的序列化
缓存注解
@Cacheable - 缓存查询结果
@Cacheable(prefix = "order", key = "#orderId", ttl = 600)
public OrderDTO getOrderById(Long orderId) {
// 业务逻辑
}
参数说明:
-
prefix:缓存key前缀,用于区分业务模块 -
key:缓存key,支持SpEL表达式 -
ttl:过期时间(秒),默认300秒 -
condition:缓存条件,只有满足条件才缓存 -
unless:排除条件,满足条件时不缓存 -
localCache:是否启用本地缓存,默认true -
distributedCache:是否启用分布式缓存,默认true
@CacheEvict - 清除缓存
@CacheEvict(prefix = "order", keys = {"#order.orderId"})
public OrderDTO updateOrder(OrderDTO order) {
// 业务逻辑
}
参数说明:
-
prefix:缓存key前缀 -
keys:要清除的缓存key数组,支持SpEL表达式 -
allEntries:是否清除所有相关缓存 -
condition:清除条件
@CachePut - 主动更新缓存
@CachePut(prefix = "order", key = "#result.orderId", ttl = 600)
public OrderDTO createOrder(OrderDTO order) {
// 业务逻辑
}
使用示例
1. 基本缓存使用
@Service
public class OrderService {
// 缓存订单信息,10分钟过期
@Cacheable(prefix = "order", key = "#orderId", ttl = 600)
public OrderDTO getOrderById(Long orderId) {
// 调用gRPC服务获取订单信息
return callGrpcService(orderId);
}
}
2. 条件缓存
// 只有当orderId大于0时才缓存,且结果不为空时才缓存
@Cacheable(
prefix = "order",
key = "#orderId",
ttl = 600,
condition = "#orderId > 0",
unless = "#result == null"
)
public OrderDTO getOrderByIdWithCondition(Long orderId) {
return getOrderById(orderId);
}
3. 复杂key表达式
// 使用多个参数组合作为缓存key
@Cacheable(
prefix = "order",
key = "'customer_' + #customerId + '_status_' + #status + '_page_' + #page",
ttl = 300
)
public List<OrderDTO> getOrdersByCustomer(Long customerId, String status, int page, int size) {
return callGrpcService(customerId, status, page, size);
}
4. 缓存失效
// 更新订单时清除相关缓存
@CacheEvict(prefix = "order", keys = {
"#order.orderId",
"'customer_' + #order.customerId + '_*'"
})
public OrderDTO updateOrder(OrderDTO order) {
return callGrpcUpdateService(order);
}
// 清除所有订单相关缓存
@CacheEvict(prefix = "order", allEntries = true)
public void batchUpdateOrders(List<OrderDTO> orders) {
callGrpcBatchUpdateService(orders);
}
5. 主动缓存更新
// 创建订单后主动设置缓存
@CachePut(prefix = "order", key = "#result.orderId", ttl = 600)
public OrderDTO createOrder(OrderDTO order) {
return callGrpcCreateService(order);
}
配置说明
application.properties配置
# 缓存配置
cache.enabled=true
cache.default-ttl=300
# Caffeine本地缓存配置
cache.caffeine.initial-capacity=100
cache.caffeine.maximum-size=10000
cache.caffeine.expire-after-write=60
cache.caffeine.expire-after-access=0
cache.caffeine.record-stats=true
# Redis缓存配置
cache.redis.enabled=true
cache.redis.host=localhost
cache.redis.port=6379
cache.redis.password=123456
cache.redis.database=11
cache.redis.timeout=2000
# 缓存监控配置
cache.metrics.enabled=true
cache.metrics.step=1m
缓存时间建议
- 用户基础信息:3600秒(1小时)
- 商品信息:1800秒(30分钟)
- 订单列表:300秒(5分钟)
- 统计数据:60秒(1分钟)
- 配置数据:86400秒(24小时)
监控和管理
健康检查
访问:/actuator/health
{
"status": "UP",
"components": {
"cache": {
"status": "UP",
"details": {
"status": "缓存服务正常"
}
}
}
}
缓存统计
访问:/actuator/cache/stats
{
"localHits": 1250,
"localMisses": 180,
"redisHits": 450,
"redisMisses": 80,
"totalRequests": 1960,
"hitRate": 0.867,
"localSize": 256
}
缓存管理操作
# 清除指定缓存
DELETE /actuator/cache/evict?key=order:12345
# 清除前缀缓存
DELETE /actuator/cache/evict/prefix?prefix=order
# 清空所有缓存
DELETE /actuator/cache/clear
# 检查缓存是否存在
GET /actuator/cache/exists?key=order:12345
# 获取缓存过期时间
GET /actuator/cache/expire?key=order:12345
最佳实践
1. 缓存key设计原则
- 唯一性:确保key的唯一性,避免冲突
- 可读性:key应该具有一定的可读性,便于调试
- 简洁性:key不宜过长,影响性能
- 层次性:使用冒号分隔符建立层次结构
// 好的key设计
"order:12345"
"customer:67890:orders"
"product:category:123:page:1"
// 不好的key设计
"very_long_business_module_name_order_information_12345"
"order12345customerinfo"
2. 缓存时间策略
- 高频查询、更新不频繁:长时间缓存(10-30分钟)
- 中等频率数据:中等时间缓存(5-10分钟)
- 实时性要求高:短时间缓存(1-5分钟)
- 配置类数据:长时间缓存(1-24小时)
3. 缓存失效策略
// 单个数据更新
@CacheEvict(prefix = "order", keys = "#order.orderId")
public OrderDTO updateOrder(OrderDTO order) { ... }
// 影响多个缓存的操作
@CacheEvict(prefix = "order", keys = {
"#order.orderId",
"'customer_' + #order.customerId + '_*'"
})
public OrderDTO updateOrderStatus(OrderDTO order) { ... }
// 批量操作清除所有相关缓存
@CacheEvict(prefix = "order", allEntries = true)
public void batchUpdateOrders(List<OrderDTO> orders) { ... }
4. 条件缓存使用
// 参数验证
condition = "#orderId != null && #orderId > 0"
// 结果验证
unless = "#result == null || #result.isEmpty()"
// 业务条件
condition = "#status == 'ACTIVE'"
注意事项
1. 数据类型支持
- ✅ Lombok @Data类:完全支持,推荐使用
- ✅ 标准POJO:支持
- ✅ 基本数据类型:支持
- ✅ 集合类型:支持
- ❌ Protobuf对象:需要转换为DTO再缓存
2. 缓存穿透防护
系统自动处理null值缓存,防止缓存穿透:
// 查询结果为null时也会被缓存(使用特殊标记)
@Cacheable(prefix = "order", key = "#orderId", ttl = 300)
public OrderDTO getOrderById(Long orderId) {
OrderDTO order = callGrpcService(orderId);
return order; // 即使为null也会被缓存
}
3. 异常处理
缓存操作异常不会影响业务执行:
- 缓存读取失败 → 直接执行业务方法
- 缓存写入失败 → 记录日志,继续执行
- 缓存清除失败 → 记录日志,不影响业务
4. 性能考虑
- 本地缓存优先:优先查询Caffeine,未命中再查Redis
- 异步回写:本地缓存命中但Redis缺失时,异步回写Redis
- 批量操作:使用SCAN代替KEYS,避免阻塞Redis
故障排查
1. 缓存未命中
检查项:
- key生成是否正确
- TTL是否过短
- 条件表达式是否满足
- 缓存服务是否正常
2. 缓存数据异常
检查项:
- 序列化配置是否正确
- 数据类型是否支持
- JSON格式是否有效
3. 性能问题
检查项:
- 缓存命中率是否正常(建议>70%)
- 缓存大小是否合理
- Redis连接是否正常
示例代码
完整的使用示例请参考:
-
CachedOrderService.java- 服务层缓存使用示例 -
CachedOrderController.java- 控制器层使用示例 -
OrderDTO.java- Lombok @Data类示例
总结
通过本缓存功能,可以显著提升应用性能:
- 响应时间减少:60-80%(缓存命中时)
- 网络调用减少:70-90%(针对gRPC调用)
- 系统稳定性提升:降级机制保证服务可用性
推荐在以下场景使用缓存:
- ✅ 高频查询的gRPC接口
- ✅ 相对稳定的基础数据
- ✅ 对响应时间敏感的用户接口
- ❌ 强一致性要求的实时数据
- ❌ 频繁变更的业务数据