957 字
约 3 分钟
7
S08-J03 · 缓存:一级/二级与失效

适用读者:以为"开了二级缓存就一定快"的人。
前置:S08-B01。
覆盖旧文:897(86) 902(74) + 空壳 1144 补写(E-S08-013)= 160 点。
上一篇:S08-J02 · 插件与拦截器链
下一篇:S08-J04 · 批量与注入器。


1. 一句话结论

一级缓存是 SqlSession 级(默认开),二级缓存是 Mapper 级(需手动开)。它们的失效条件与"能不能用"都取决于事务与更新动作。

级别 作用域 默认 共享
一级 同一个 SqlSession 开启 ❌
二级 同一个 namespace(Mapper) 关闭(需 <cache/>) ✅(同 JVM)

Spring 环境下的关键事实:SqlSessionTemplate 每次操作都会关闭 SqlSession(除非在事务内),所以一级缓存"看起来没用"——因为它只在同一事务/会话内有效。


2. 一级缓存的失效条件

动作 是否清空一级缓存
同一 SqlSession 内再次相同查询 ✅ 命中
执行 insert/update/delete 清空
sqlSession.clearCache() 清空
SqlSession 关闭(Spring 下每次操作后) 缓存没了

判据:"这两次查询在同一个事务里吗?" 不在 → 一级缓存不生效。


3. 二级缓存的三个硬约束

约束 原因
必须 implements Serializable 缓存对象可能被序列化
必须在同一个 namespace 缓存 key 含 statementId
跨表更新不会自动清空 只能清自己 namespace 的缓存

最后一条是最大的坑:A 表更新不会清 B 表的二级缓存 → 脏读。

场景 二级缓存
单表、读多写少、可容忍轻微不一致 可用
多表关联查询 别开(更新无法联动清理)
分布式/多实例 别开(各 JVM 独立缓存)

4. 延迟加载(A-902)

配置 行为
lazyLoadingEnabled=true 关联对象按需加载
aggressiveLazyLoading=false 不用到就不加载

原理:返回代理对象,访问属性时才发第二条 SQL。

坑:延迟加载在事务外访问会抛 LazyInitializationException(与 JPA 同款问题)——接口返回实体时最容易踩。


5. 实践建议

建议 理由
优先用 Redis 等外部缓存 跨实例、可控失效
MyBatis 缓存只做"会话内优化" 一级缓存天然如此
二级缓存慎用 失效难控
缓存对象用 DTO 避免序列化与懒加载问题

6. 五个误区

误区 真相
"一级缓存默认全局生效" 只在同一 SqlSession/事务内
"开了二级缓存就快" 失效问题可能带来脏数据
"更新会自动清所有缓存" 只清当前 namespace
"延迟加载总是更好" 事务外访问会抛异常
"MyBatis 缓存能替代 Redis" 不跨实例

7. 自检

  1. 一级缓存的作用域与失效条件?
  2. Spring 下为什么"感觉一级缓存没用"?
  3. 二级缓存的三条硬约束?
  4. 跨表更新对二级缓存有什么影响?
  5. 延迟加载在什么场景会抛异常?

8. 下一篇

S08-J04 · 批量操作与 SQL 注入器

缓存讲完,进阶层最后一篇:批量插入的三种写法与性能差异,以及"SQL 注入器"这个 MP 扩展点。


本篇属于系列《S08 · MyBatis 持久层》。旧文来源:A-897/902(空壳 1144 补写)。

S08-J03 · 缓存:一级/二级与失效
https://www.clxhxhhr.top/posts/5289/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。