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操作。 - 手动调用
Cache的clear()方法。
- 执行同命名空间的
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 扩展缓存能力,适应分布式场景。
最佳实践:
- 优先使用一级缓存,避免滥用二级缓存。
- 高并发场景下,结合第三方缓存提升扩展性。
- 定期监控缓存命中率和内存使用,优化配置参数。