MyBatis 一级缓存、二级缓存
·
一、一级缓存(本地缓存,默认开启,无需手动配置)
1. 作用范围
缓存存放在当前 SqlSession 内部,每个 SqlSession 独立一份缓存,不同 SqlSession 之间互不共享。
2. 底层存储
底层是一个 HashMap,key 由「statementId + 分页参数 + SQL 语句 + 查询参数 + 环境 id」拼接组成,value 是查询出来的结果对象。
3. 生效场景
同一个 SqlSession 内,执行完全相同的查询 SQL、相同参数,第二次查询不会访问数据库,直接从本地 Map 拿数据。 示例:
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.selectById(1); // 查库,放入一级缓存
mapper.selectById(1); // 同session,直接走缓存
4. 一级缓存失效场景(高频考点)
- SqlSession 执行了
insert/update/delete增删改操作,会清空当前 session 全部一级缓存; - 手动调用
session.clearCache()手动清空; - 两次查询中间执行了不同的查询语句、不同参数;
- 关闭、提交、回滚当前 SqlSession,缓存直接销毁。
5. 缺陷
多线程、多 SqlSession 环境下会出现脏读: SessionA 查询 id=1 存入缓存,SessionB 更新 id=1 并提交,SessionA 再次查询还是读取旧缓存数据,拿不到最新库中数据。
二、二级缓存(全局 Mapper 缓存,默认关闭,需手动开启)
1. 作用范围
作用域是同一个 Mapper(同一个 namespace),多个 SqlSession 共享这一块缓存,生命周期跟随整个应用。
2. 开启步骤
- 全局配置开启总开关:
<setting name="cacheEnabled" value="true"/> - 在对应 Mapper.xml 中添加
<cache/>标签开启当前 namespace 二级缓存; - 查询返回的实体类必须实现
Serializable序列化接口,二级缓存会将对象序列化存入磁盘 / 内存。
3. 执行查询流程(二级缓存优先级)
- 执行查询时,先查当前 Mapper 的二级缓存;
- 二级缓存无数据,再去当前 SqlSession 一级缓存查找;
- 一级缓存也没有,才访问数据库;
- 查询结果先存入一级缓存,SqlSession 提交 / 关闭后,数据才会同步到二级缓存。
4. 二级缓存清空时机
当同一个 namespace 下执行 insert/update/delete,会清空该 Mapper 下全部二级缓存,保证数据一致性。
5. 优缺点
优点:跨 SqlSession 共享缓存,大幅减少数据库请求; 缺点:
- 跨 Mapper 操作同一张表时无法感知缓存更新,容易脏读; 比如 UserMapper 查用户存入二级缓存,OrderMapper 更新用户表,不会清空 UserMapper 二级缓存;
- 必须实现序列化,开发多一步操作;
- 多表关联场景不推荐使用。
三、一级缓存 vs 二级缓存核心对比
- 作用域 一级:单个 SqlSession;二级:同一个 Mapper 命名空间,全局共享
- 默认状态 一级:默认开启;二级:默认关闭,需手动配置
- 共享性 一级不共享;二级多会话共享
- 数据存储 一级存内存 HashMap,无需序列化;二级需要序列化存储
- 清空规则 一级:当前 session 增删改就清;二级:当前 namespace 增删改才清
- 脏读风险 一级:多 session 并发更新会脏读;二级:跨 Mapper 修改同表会脏读
四、生产环境使用建议
- 一级缓存不用管,默认开启,单会话重复查询天然优化;
- 二级缓存线上谨慎使用:
- 单表简单 CRUD、极少更新的基础字典表可以开启;
- 多表关联、多 Mapper 操作同一张业务表,直接关闭二级缓存,避免脏数据;
- 高并发实时数据场景,不要依赖 MyBatis 二级缓存,推荐使用 Redis 做分布式缓存,可控性更强。
简短总结
一级缓存是 SqlSession 本地缓存,默认开启,同会话重复查询生效,会话内增删改自动清空,多会话存在脏读;二级缓存是 Mapper 级共享缓存,需要手动开启、实体序列化,多 SqlSession 共享,仅当前命名空间更新才清空缓存,跨 Mapper 更新会出现脏读,复杂业务一般不推荐使用二级缓存。
更多推荐




所有评论(0)