MyBatis一级缓存是SqlSession级私有缓存,默认开启,同会话内重复查询直接返回缓存,执行DML即清空;二级缓存是Mapper级共享缓存,需手动配置,commit后写入,DML操作清空对应namespace全部缓存。

MyBatis 一级缓存和二级缓存,是它内置的两层本地缓存机制,核心目标很实在:减少重复 SQL 查询、降低数据库压力、提升读取性能。它们不是可有可无的附加功能,而是影响数据一致性与系统行为的关键设计点,用对了提效,用错了容易出脏数据。
一级缓存:SqlSession 级别的“私有小本本”
一级缓存默认开启,无需配置,作用域严格限定在**同一个 SqlSession 实例内**。它底层是一个 PerpetualCache(本质是 HashMap),由 SqlSession 内部的 Executor 持有。
- 同一次 SqlSession 中执行两次完全相同的查询(SQL + 参数 + 环境一致),第一次查库并写入缓存,第二次直接返回缓存结果
- 只要在该 SqlSession 中执行过任意 insert/update/delete 操作,不管是否 commit,整个一级缓存都会被清空
- 不同 SqlSession 之间互不共享,哪怕查的是同一张表、同一个 ID,也各自维护独立缓存
- 常见陷阱:事务未提交时,另一个线程用新 SqlSession 查询,看到的仍是旧数据——因为一级缓存不跨会话,也不感知外部变更
二级缓存:Mapper 级别的“公共公告栏”
二级缓存作用域是 **Mapper 的 namespace**,多个 SqlSession 可以共享同一份缓存数据,但需要手动开启,且有明确前提条件。
- 必须在 MyBatis 全局配置中启用:
- 必须在对应 Mapper XML 文件中添加
或 - 所查实体类必须实现
Serializable接口(因缓存需序列化存储) - 只有在 SqlSession 调用
close()或commit()后,查询结果才会真正写入二级缓存;未提交的查询不参与二级缓存写入 - 同 namespace 下任意增删改操作,会清空该 namespace 对应的整个二级缓存区域
缓存查询顺序与失效逻辑
当一个查询发起时,MyBatis 的实际查找路径是:先查二级缓存 → 再查一级缓存 → 最后查数据库。写入则相反:结果先存一级缓存,commit/close 后再同步到二级缓存。
- 缓存 key 由多要素构成:MappedStatement ID + SQL 语句 + 参数值 + 分页参数 + 环境 ID,确保精准命中
- 所有 DML 操作(insert/update/delete)都会触发对应 namespace 的缓存失效,但不会影响其他 namespace
- 一级缓存失效是“全清”,二级缓存失效也是“全清”,不支持按 key 精细剔除(除非自定义 Cache 实现)
什么时候该用、不该用?
缓存不是银弹。适合缓存的数据通常具备这些特征:读多写少、实时性要求不高、变更影响范围可控。
- 适合:省市区字典表、状态码枚举、配置项、用户基本信息(如昵称、头像 URL)
- 慎用:账户余额、库存数量、订单状态、涉及强一致性的业务场景
- 禁用:高并发写频繁的表、存在跨服务更新的表(二级缓存无法感知外部变更)
- 分布式环境下建议用 Redis 等第三方缓存替代二级缓存,避免节点间数据不一致
基本上就这些。缓存机制不复杂,但细节决定成败——尤其是失效时机和作用域边界,稍不注意就会埋下数据不一致的隐患。










