MyBatis 的缓存机制

120 阅读3分钟

MyBatis 的缓存机制分为一级缓存和二级缓存,通过合理配置可显著提升性能,但也需注意数据一致性和适用场景。以下是详细的解析:


一、一级缓存

1. 核心特性

  • 作用范围SqlSession 级别,同一会话内的多次相同查询会复用缓存结果。

  • 默认开启:无需配置,自动生效。

  • 缓存策略:基于 PerpetualCache 实现,底层为 HashMap

  • 失效条件

    • 执行 INSERT/UPDATE/DELETE 操作。
    • 调用 SqlSession.clearCache()
    • SqlSession 关闭。

2. 示例场景

try (SqlSession session = sqlSessionFactory.openSession()) {
    UserMapper mapper = session.getMapper(UserMapper.class);
    User user1 = mapper.selectUserById(1); // 第一次查询数据库
    User user2 = mapper.selectUserById(1); // 命中一级缓存,直接返回结果
}

3. 最佳实践

  • 适用场景:短时、高频的重复查询(如单次请求内的多次相同查询)。
  • 注意事项:避免在长事务中因缓存未更新导致脏读。

二、二级缓存

1. 核心特性

  • 作用范围:跨 SqlSession,多个会话共享缓存。

  • 手动开启:需在 Mapper XML 中添加 <cache/> 标签。

  • 序列化要求:实体类需实现 Serializable 接口(若使用默认缓存)。

  • 失效条件

    • 执行同命名空间的 INSERT/UPDATE/DELETE 操作。
    • 手动调用 Cacheclear() 方法。

2. 配置方式

<mapper namespace="com.example.UserMapper">
    <cache/> <!-- 启用二级缓存 -->
    <select id="selectUserById" resultType="User">...</select>
</mapper>

3. 高级配置参数

<cache
  eviction="LRU"             <!-- 淘汰策略LRU/FIFO/SOFT/WEAK-->
  flushInterval="60000"      <!-- 自动刷新间隔(毫秒) -->
  size="1024"                <!-- 最大缓存对象数 -->
  readOnly="true"            <!-- 是否只读(建议为false以支持序列化修改) -->
/>

4. 示例场景

// Session 1
try (SqlSession session1 = sqlSessionFactory.openSession()) {
    UserMapper mapper1 = session1.getMapper(UserMapper.class);
    User user1 = mapper1.selectUserById(1); // 查询数据库并缓存
}
​
// Session 2
try (SqlSession session2 = sqlSessionFactory.openSession()) {
    UserMapper mapper2 = session2.getMapper(UserMapper.class);
    User user2 = mapper2.selectUserById(1); // 命中二级缓存
}

5. 最佳实践

  • 适用场景:读多写少的数据(如配置信息、静态数据)。

  • 注意事项

    • 避免频繁更新的数据使用二级缓存,防止缓存频繁失效。
    • 分布式环境中建议集成 Redis 或 Ehcache。

三、缓存机制对比

维度一级缓存二级缓存
作用范围SqlSession 内部SqlSession(全局共享)
存储位置内存(SqlSession 对象内)内存或外部存储(如 Redis)
开启方式默认开启需手动配置 <cache/>
序列化要求需要(默认缓存)
失效策略事务操作触发自动失效同命名空间更新操作触发失效

四、第三方缓存集成

1. 集成 Ehcache

  • 步骤 1:添加依赖:

    <dependency>
      <groupId>org.mybatis.caches</groupId>
      <artifactId>mybatis-ehcache</artifactId>
      <version>1.2.2</version>
    </dependency>
    
  • 步骤 2:配置 Mapper XML:

    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
    

2. 集成 Redis

  • 步骤 1:添加依赖(如使用 Redisson):

    <dependency>
      <groupId>org.redisson</groupId>
      <artifactId>redisson</artifactId>
      <version>3.16.1</version>
    </dependency>
    
  • 步骤 2:自定义缓存实现类:

    public class RedisCache implements Cache {
        // 实现 Cache 接口方法,使用 Redis 客户端操作
    }
    
  • 步骤 3:配置 Mapper XML:

    <cache type="com.example.RedisCache"/>
    

五、缓存问题与调试

1. 常见问题

  • 脏读:多个会话更新同一数据时,需确保缓存及时失效。
  • 内存泄漏:缓存对象未正确释放,需合理设置淘汰策略和缓存大小。
  • 序列化异常:未实现 Serializable 导致缓存失败。

2. 调试方法

  • 日志监控:开启 MyBatis 日志,观察 SQL 执行情况。

    <settings>
      <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    
  • 缓存命中统计:集成监控工具(如 Druid)查看缓存命中率。


六、总结

  • 一级缓存:提升单会话内重复查询效率,但需注意事务边界。
  • 二级缓存:适合全局共享的静态数据,需权衡数据一致性与性能。
  • 第三方缓存:通过集成 Ehcache 或 Redis 扩展缓存能力,适应分布式场景。

最佳实践

  • 优先使用一级缓存,避免滥用二级缓存。
  • 高并发场景下,结合第三方缓存提升扩展性。
  • 定期监控缓存命中率和内存使用,优化配置参数。