mybatis缓存需按业务特征精准配置:一级缓存默认开启、sqlsession级、执行增删改或关闭会话即失效;二级缓存需显式启用、mapper级、跨会话共享,但要求全局开关打开、mapper声明缓存、实体实现serializable,且写操作会清空整个namespace缓存。

MyBatis 缓存不是“开了就快”,而是要结合业务读写特征、数据一致性要求和运行环境来精准配置。一级缓存默认生效但作用域窄,二级缓存需显式开启但共享性强——关键在分清场景、配对失效逻辑、避开常见陷阱。
一级缓存:用好默认能力,别依赖跨会话复用
它天然绑定 SqlSession,无需配置,同一会话内相同 SQL(含完全一致的参数类型、值、分页参数)第二次查直接走内存 HashMap。但要注意:
- 执行 INSERT/UPDATE/DELETE 后,当前 SqlSession 的所有一级缓存立即清空
- 事务 commit 或 rollback、SqlSession close 都会让缓存失效
- Spring 中加了 @Transactional 的方法,整个事务共用一个 SqlSession,此时一级缓存能跨多个 DAO 调用生效;没加事务时,每次 Service 调用新建 SqlSession,缓存无法延续
- 参数类型不一致(如 Integer(1) vs String("1"))会生成不同 CacheKey,导致缓存未命中——建议统一入参类型或做预处理
二级缓存:按需启用,严守前提条件
它面向 Mapper namespace,多个 SqlSession 可共享结果,但必须满足三个硬性条件:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 全局开关打开:
<setting name="cacheEnabled" value="true"></setting>(mybatis-config.xml) - 对应 Mapper 显式声明:
<cache></cache>(XML)或@CacheNamespace(注解) - 查询返回的实体类必须实现
Serializable接口(否则反序列化失败,缓存写入静默失败)
启用后,默认所有 <select></select> 都走二级缓存;若某条 SQL 不想缓存,加 useCache="false" 即可。注意:任意对该 Mapper 执行写操作(哪怕只改一行),整个 namespace 的二级缓存全清,不是局部刷新。
选型与调优:根据数据特性决定缓存策略
不是所有数据都适合缓存,优先考虑以下场景:
- 读多写少:地区码表、字典项、系统配置等,修改频率低,缓存有效期长
- 允许弱一致性:用户昵称、商品分类等可容忍几秒延迟,不适用于账户余额、库存数量等强一致场景
- 查询开销大:含多表 JOIN、GROUP BY、子查询的报表类 SQL,缓存收益明显
- 对象体积适中:二级缓存默认基于内存(PerpetualCache),避免缓存超大对象导致 GC 压力;如需更大容量或高可用,可集成 Redis/EhCache,但需额外配置序列化与过期策略
避坑要点:几个高频失效原因
缓存没生效?大概率卡在这几个地方:
- Mapper 方法没写
@Select注解或 XML 里没定义<select></select>标签 → 不进入缓存流程 - 用了 Spring 的
@Transactional,但事务传播行为是REQUIRES_NEW→ 每次新开 SqlSession,一级缓存失效 - 二级缓存开启但实体未实现
Serializable→ 缓存写入失败,日志可能无报错,表现为“像没开一样” - 一级缓存内多次获取同一对象并修改其属性 → 后续读取拿到的是被改过的引用,造成脏数据(建议查询后做深拷贝或禁用对象复用)
- 读写分离环境下,从库延迟导致刚写完就查,一级缓存虽命中但数据旧 → 可设
localCacheScope=STATEMENT关闭单语句级缓存,强制走库
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










