Skip to content

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 层默认不开。

基于 VitePress 重建