【Mytais系列】缓存机制:一级缓存、二级缓存
MyBatis 的缓存机制是其性能优化的重要特性,分为 一级缓存(本地缓存) 和 二级缓存(全局缓存),两者在作用范围、生命周期和使用场景上有显著差异。
一、一级缓存(Local Cache)
1. 核心特性
特性 | 说明 |
作用范围 | SqlSession 级别(同一个会话内有效) |
默认状态 | 自动开启,无需配置 |
存储位置 | 内存中(BaseExecutor 中的 |
失效条件 | 执行 |
2. 工作原理
3. 注意事项
- 失效场景:跨 SqlSession 或执行写操作后缓存失效。
- 强制刷新:可通过
sqlSession.clearCache()
手动清空。 - 作用域隔离:不同 SqlSession 的缓存互不影响。
二、二级缓存(Global Cache)
1. 核心特性
特性 | 说明 |
作用范围 | Mapper 级别(跨 SqlSession 共享) |
默认状态 | 默认关闭,需手动配置 |
存储位置 | 内存或第三方缓存(如 Redis、Ehcache) |
失效条件 | 执行 |
2. 配置方式
- 全局启用(mybatis-config.xml):
<settings><setting name="cacheEnabled" value="true"/>
</settings>
- Mapper 启用(Mapper.xml):
<mapper namespace="com.example.UserMapper"><cache eviction="LRU" <!-- 回收策略(LRU/FIFO等) -->flushInterval="60000" <!-- 刷新间隔(毫秒) -->size="1024" <!-- 最大缓存对象数 -->readOnly="true"/> <!-- 是否只读 -->
</mapper>
- 实体类序列化:
public class User implements Serializable { /* 字段 */ }
3. 工作原理
(应用1、应用2:同一个应用程序进程内的不同操作单元(例如:两个 HTTP 请求线程、两个业务方法调用等)。)
具体示例说明,场景:用户查询优化
// 线程1(应用1)
try (SqlSession sqlSession1 = sqlSessionFactory.openSession()) {UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);User user1 = mapper1.getUserById(1); // 首次查询,访问数据库并缓存结果
}// 线程2(应用2)
try (SqlSession sqlSession2 = sqlSessionFactory.openSession()) {UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);User user2 = mapper2.getUserById(1); // 命中二级缓存,直接返回结果
}
- 结果:
user2
直接从二级缓存获取数据,无需访问数据库
4. 注意事项
- 事务提交:只有事务提交后,二级缓存才会更新。
- 缓存穿透:若多个 Mapper 共享缓存(如跨 namespace),需使用
<cache-ref>
。 - 第三方缓存:可通过集成 Redis 或 Ehcache 实现分布式缓存。
三、一级缓存 vs 二级缓存
对比维度 | 一级缓存 | 二级缓存 |
作用范围 | SqlSession 内部 | 跨 SqlSession(Mapper 级别) |
生命周期 | 随 SqlSession 销毁而清除 | 应用生命周期(除非手动清除或过期) |
数据共享 | 无法共享 | 多个 SqlSession 共享 |
性能影响 | 高频重复查询优化 | 跨会话重复查询优化 |
适用场景 | 短时、高频的重复查询(如循环内查询) | 长时间不变的数据(如配置表) |
风险点 | 脏读(同一会话内数据不一致) | 分布式环境数据一致性需额外处理 |
MyBatis SELECT 语句执行流程中一级缓存和二级缓存的查询顺序:
四、缓存最佳实践
- 一级缓存:
-
- 避免在长会话中执行大量查询(可能内存溢出)。
- 写操作后及时提交事务,确保缓存更新。
- 二级缓存:
-
- 只缓存读多写少的数据(如配置表、历史数据)。
- 分布式环境建议禁用默认缓存,改用 Redis。
- 复杂对象确保实现
Serializable
接口。 - 通过
<cache blocking="true">
防止缓存击穿。
- 调试技巧:
-
- 日志中搜索
Cache Hit Ratio
查看缓存命中率。 - 使用
@Options(flushCache = true)
强制刷新缓存。
- 日志中搜索
通过合理使用缓存机制,可以显著减少数据库访问次数,但需权衡数据一致性和性能需求。