P70 为什么大厂弃用 MyBatis 二级缓存?
面试题:MyBatis 有二级缓存,为什么大厂都默认关闭/不用?
1. 先分清两级缓存
- 一级缓存:SqlSession 级别,默认开启,同一 SqlSession 内相同 SQL 直接命中;
- 二级缓存:Mapper 级别(namespace),默认关闭,跨 SqlSession 共享,需
cache标签开启;- 缓存的是查询结果对象,可配置 LRU/FIFO 等淘汰策略。
2. 为什么大厂不用二级缓存
① 一致性难保证(最致命)
二级缓存不知道其他系统/进程/直改 DB 的修改:
- 多个应用实例共享同一数据库,MyBatis 二级缓存是应用本地缓存,实例间不互通 → A 实例缓存了旧数据,B 实例更新了 DB,A 还在给旧数据;
- 绕过 MyBatis 的修改(其他服务、定时任务直连 DB、DBA 手改)无法感知;
- 一旦脏读,出的是线上事故级问题。
② 缓存粒度是 namespace,不是表
一个 Mapper 对应一个缓存空间,但 SQL 往往跨表 join:
text
OrderMapper 缓存了 join User 的结果
User 表被别的 Mapper 更新 → OrderMapper 缓存不知道 → 脏数据③ 收益有限
- 大厂通常已有 Redis/本地缓存(Caffeine) 做业务级缓存,命中率更高、可控性更强;
- 二级缓存省的是 MyBatis 查询,但热点数据业务层早就缓存了;
- 配置/维护成本与风险不成正比。
④ 与分布式环境冲突
- 分布式部署下"本地缓存"天然不共享,一致性需要额外机制(如失效广播),MyBatis 二级缓存没有这个能力。
3. 大厂替代方案
- 业务层缓存:Caffeine(本地)+ Redis(分布式),Cache-Aside 模式;
- 明确缓存一致性策略:更新 DB 后删缓存、TTL 兜底;
- 只对读多写少、一致性要求低的数据做缓存。
4. 加分点
- 能说出"二级缓存默认关闭"以及开启方式(
<cache/>+cache-enabled=true); - 指出"缓存的是对象引用还是序列化副本":默认存对象,改对象可能污染缓存;
- 追问"一级缓存也有坑":跨 SqlSession 不共享、动态 SQL 哈希判断、批量更新不自动清缓存(要手动 flushCache)。
一句话总结
MyBatis 二级缓存是应用本地、namespace 粒度的缓存,跨实例/跨表更新都无法感知,一致性风险大、收益有限;大厂用业务层 Caffeine/Redis 缓存替代,配合明确的失效策略,MyBatis 层默认不开。