MyBatis 缓存机制是其性能优化的一大亮点,主要分为 一级缓存(Local Cache) 和 二级缓存(Global Cache)。下面分别从原理、特性、使用方式等维度深入讲解:
一、一级缓存(Local Cache)
1. 概念
-
一级缓存是 SqlSession 级别 的缓存,默认开启且无法关闭。
-
同一个 SqlSession 中执行的 相同 SQL 查询,MyBatis 会先从缓存中取结果,而不是再次访问数据库。
2. 缓存作用域
-
仅限当前
SqlSession,不同SqlSession不共享。 -
SqlSession 一关闭,缓存就会被清除。
3. 生效条件(缓存命中)
以下情况满足才会命中缓存:
-
相同的 statementId + SQL + 参数
-
同一个 SqlSession
-
中间没有执行过更新操作(增删改)
4. 失效场景
-
执行了
update/insert/delete操作,会清空该 SqlSession 的缓存。 -
手动调用
clearCache()。 -
SqlSession 关闭。
✅ 使用示例:
SqlSession session = sqlSessionFactory.openSession();
User u1 = session.selectOne("getUserById", 1); // 查询数据库
User u2 = session.selectOne("getUserById", 1); // 命中一级缓存
二、二级缓存(Global Cache)
1. 概念
-
二级缓存是 mapper namespace 级别 的缓存(可视作多个 SqlSession 共享的缓存)。
-
默认 不启用,需要手动开启。
-
二级缓存的作用范围是 同一个 Mapper 接口的所有 SqlSession 实例。
2. 开启方式
a. 开启全局配置(mybatis-config.xml):
<setting name="cacheEnabled" value="true"/>
b. Mapper XML 中配置缓存:
<cache/>
c. 实体类必须实现 Serializable 接口。
3. 缓存触发 & 清理机制
-
查询结果会被存入二级缓存(前提是当前 SqlSession 提交后),被其他 SqlSession 命中。
-
若有任意更新操作(增删改),会清除当前 namespace 下的二级缓存。
✅ 示例:
// 第一次查询,缓存为空,从数据库加载
try (SqlSession session1 = sqlSessionFactory.openSession()) {
User user1 = session1.selectOne("getUserById", 1);
session1.commit(); // 必须 commit,缓存才写入二级缓存
}
// 第二次查询,从二级缓存读取(无需数据库)
try (SqlSession session2 = sqlSessionFactory.openSession()) {
User user2 = session2.selectOne("getUserById", 1);
}
三、缓存结构与原理(核心类)
-
MyBatis 缓存结构本质是一个装饰器链:
CachingExecutor -> BaseExecutor -> ... -
默认缓存实现类为:
PerpetualCache(底层就是 HashMap)。 -
可以通过
<cache type="xxx">指定自定义缓存实现。
四、自定义缓存实现(可选)
MyBatis 支持插件式缓存机制,可自定义缓存实现类:
<cache type="com.example.MyCustomCache"/>
只需实现接口:org.apache.ibatis.cache.Cache
五、一级 vs 二级缓存对比
| 特性 | 一级缓存 | 二级缓存 |
|---|---|---|
| 生命周期 | SqlSession 生命周期 | Mapper 生命周期 |
| 是否默认开启 | 是 | 否 |
| 是否可配置 | 否 | 是(支持定制) |
| 并发支持 | 不需要(线程独占) | 可支持(需线程安全) |
| 清除机制 | 增删改操作 / 手动清空 | 同 Mapper 增删改即清空 |
六、缓存使用建议
✅ 建议开启二级缓存的情况:
-
读多写少的场景
-
查询代价较高、变化不频繁的业务,如字典表、配置表
❌ 不建议开启:
-
实时性要求高的系统
-
数据频繁更新,缓存可能造成脏读
七、常见问题
-
为什么二级缓存没有生效?
-
没有调用
SqlSession.commit()(缓存不会写入) -
查询对象未实现
Serializable -
XML 中未配置
<cache/> -
有增删改清空了缓存
-
-
如何与 Redis 整合二级缓存?
八、总结一句话
一级缓存提升了SqlSession 级别的查询效率,二级缓存则在全局层面复用查询结果。合理开启缓存机制能显著减少数据库访问、提升性能,但需结合业务实际,避免脏读和缓存不一致问题。
如需深入:我可以继续讲讲如何结合 Redis、Spring 缓存机制实现分布式缓存,或者剖析 MyBatis 缓存的底层源码(如 Cache 接口、装饰器模式等)。是否继续?

7万+

被折叠的 条评论
为什么被折叠?



