核心概念

  1. 一级缓存(本地缓存)

    • SqlSession 级别缓存
    • 默认开启,同一个 SqlSession 内有效
    • 执行 DML 操作(增删改)或调用 clearCache() 后失效
  2. 二级缓存(全局缓存)

    • Mapper(namespace)级别缓存
    • 需手动开启,多个 SqlSession 共享
    • 跨会话数据共享,适用于读多写少场景
  3. 第三方缓存(Redis等)

    • 分布式缓存解决方案
    • 需集成额外组件(如 mybatis-redis-cache)

操作步骤(详细配置)

1. 开启全局二级缓存
# application.yml
mybatis-plus:
  configuration:
    cache-enabled: true  # 开启二级缓存
2. Mapper 接口启用缓存
@CacheNamespace  // 关键注解
public interface UserMapper extends BaseMapper<User> {
    // 自定义方法
}
3. 实体类实现序列化
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    // 字段...
}
4. XML 文件配置缓存策略
<!-- UserMapper.xml -->
<cache
  eviction="FIFO"             <!-- 淘汰策略 -->
  flushInterval="60000"        <!-- 刷新间隔(ms) -->
  size="512"                   <!-- 缓存对象数量 -->
  readOnly="true"/>            <!-- 是否只读 -->
5. 缓存操作API
// 获取缓存对象
Cache cache = sqlSession.getCache("com.example.mapper.UserMapper");

// 手动操作缓存
cache.putObject("key", user);
cache.getObject("key");
cache.clear();  // 清空缓存

缓存策略详解

策略类型 说明 适用场景
LRU(默认) 最近最少使用 通用场景
FIFO 先进先出 顺序访问数据
SOFT 软引用(GC时回收) 内存敏感场景
WEAK 弱引用(GC立即回收) 临时缓存

常见错误与解决方案

  1. 缓存脏读

    • 现象:数据更新后缓存未刷新
    • ✅ 解决方案:
      <insert flushCache="true">  <!-- 增删改操作自动刷新缓存 -->
      
      @Options(flushCache = Options.FlushCachePolicy.TRUE)
      
  2. 序列化异常

    • 现象:NotSerializableException
    • ✅ 解决方案:
      • 确认所有实体类实现 Serializable
      • 嵌套对象也需序列化
      • 检查第三方缓存传输对象
  3. 缓存穿透

    • 现象:频繁查询不存在的数据
    • ✅ 解决方案:
      // 空结果缓存
      if(user == null) {
          cache.putObject(key, NULL_OBJECT);
      }
      

注意事项

  1. 事务隔离性

    • 二级缓存可能导致脏读,建议:
      • 事务提交后才刷新缓存
      • 高并发写场景慎用二级缓存
  2. 缓存一致性

    • 多表关联时,更新关联表需清除所有相关缓存
    • 使用@CacheNamespaceRef声明依赖Mapper
  3. 分布式环境

    • 单机二级缓存在集群中会不一致
    • 必须使用Redis等分布式缓存

使用技巧

  1. 细粒度缓存控制

    @Select("SELECT * FROM user")
    @Options(useCache = false)  // 禁用本次查询缓存
    List<User> selectAllNoCache();
    
  2. 缓存分区管理

    @CacheNamespace(implementation=CustomCache.class) 
    public interface UserMapper {
        @CacheNamespaceRef(RoleMapper.class) // 关联缓存
        User selectUserWithRoles(Long id);
    }
    
  3. 统计缓存命中率

    Cache cache = sqlSession.getCache();
    System.out.println("命中率: " + 
        cache.getHitRatio());  // 监控缓存效果
    

最佳实践与性能优化

1. 缓存选择策略
场景 推荐方案
单机简单应用 二级缓存+LRU策略
集群环境 Redis分布式缓存
高频更新数据 禁用缓存或极短过期时间
配置类静态数据 永久缓存+手动刷新
2. Redis集成
  1. 添加依赖:

    <dependency>
      <groupId>org.mybatis.caches</groupId>
      <artifactId>mybatis-redis</artifactId>
      <version>1.0.0-beta2</version>
    </dependency>
    
  2. 创建redis.properties

    host=localhost
    port=6379
    password=
    database=0
    
  3. Mapper配置:

    @CacheNamespace(implementation = RedisCache.class)
    public interface UserMapper extends BaseMapper<User> {}
    
3. 性能优化技巧
  1. 缓存粒度控制

    • 大对象拆分为小对象缓存
    • 关联对象分开缓存
  2. 冷热数据分离

    // 热数据缓存
    @CacheNamespace(size=1000)
    public interface HotDataMapper {}
    
    // 冷数据缓存
    @CacheNamespace(size=100)
    public interface ColdDataMapper {}
    
  3. 批量操作优化

    // 批量插入时清空缓存
    int batchSize = 1000;
    if(users.size() > batchSize) {
        cache.clear();  // 大批量操作前清空
    }
    

完整示例:Redis缓存配置

// 1. Mapper接口
@CacheNamespace(implementation = RedisCache.class)
public interface ProductMapper extends BaseMapper<Product> {
    
    @Select("SELECT * FROM product WHERE category = #{category}")
    List<Product> selectByCategory(String category);
}

// 2. 自定义Redis模板(可选)
public class CustomRedisCache extends RedisCache {
    public CustomRedisCache(String id) {
        super(id);
        // 自定义序列化方式
        redisConfig.setSerializer(new Jackson2JsonRedisSerializer<>(Product.class));
    }
}

// 3. 缓存手动管理
@Service
public class ProductService {
    @Autowired
    private SqlSessionTemplate sqlSessionTemplate;
    
    public void refreshProductCache(Long productId) {
        Cache cache = sqlSessionTemplate.getCache(ProductMapper.class.getName());
        cache.removeObject(productId);  // 清除特定缓存
    }
}
# application.yml 补充配置
spring:
  redis:
    host: redis-server
    port: 6379
    timeout: 3000

黄金实践原则

  1. 读多写少:缓存适合查询频率远高于更新频率的数据
  2. 时效容忍:接受一定时间的数据延迟
  3. 缓存降级:实现缓存失效时的后备方案
  4. 监控告警:集成监控系统跟踪缓存命中率(建议保持>85%)
  5. 定期维护:设置缓存自动清理策略,避免内存泄漏

通过合理配置缓存,可使查询性能提升5-10倍。但需注意:缓存不是银弹,务必结合业务场景进行压力测试!