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接口
  • ✅ 相对稳定的基础数据
  • ✅ 对响应时间敏感的用户接口
  • ❌ 强一致性要求的实时数据
  • ❌ 频繁变更的业务数据