【避坑指南】90% 开发者都忽视了!MyBatis 默认开启二级缓存,Pod 内存飙升竟是它惹的祸!
在许多 Spring Boot 项目中,我们习惯于用 MyBatis 做持久层框架。
但你知道吗?MyBatis 默认就打开了一个“隐形内存杀手”——二级缓存。
在高并发场景下,它足以让你的 Kubernetes Pod 内存直接炸掉 !
今天这篇文章,我们就彻底讲清楚这个坑:
✅ 为什么 MyBatis 会让 Pod 内存暴涨
✅ 如何合理配置 MyBatis 缓存
✅ 高并发下的生产最佳实践
✅ 一行配置,让内存直接降 70%
一、MyBatis 缓存机制全景图
MyBatis 内置两级缓存体系:
|
缓存级别 |
作用范围 |
默认状态 |
存储位置 |
特点 |
|
一级缓存(Local Cache) |
SqlSession 级别 |
✅ 默认开启 |
JVM 内存(线程安全) |
轻量缓存,每个会话独立 |
|
二级缓存(Mapper 缓存) |
Namespace 级别 |
✅ 默认开启 |
JVM 堆内存 |
多线程共享,数据常驻 |
翻译成人话:
一级缓存相当于“线程内临时记忆”,
二级缓存相当于“每个 Mapper 一个巨大仓库”。
在单体项目中,这没什么问题。
但当你上云部署到 Kubernetes,高并发 + 多副本 + Mapper 多时,这个“仓库”就成了 内存炸弹 。
⚙️ 二、默认状态揭秘:实则你根本没关掉它!
许多开发者以为:
“我没配置 cache-enabled,应该默认是关的吧?”
❌ 实际上 MyBatis 在源码里已经写死了:
this.cacheEnabled = true; // 默认开启
也就是说:
只要你不手动关闭,它就在默默吃你的内存。
Spring Boot 的 MyBatis 自动装配并不会覆盖这个默认行为,
这意味着每个 Mapper 的查询结果(ResultMap)都可能被缓存到内存中。
三、高并发场景下的“内存雪崩”
举个简单的例子:
- 你有 30 个 Mapper
- 每个 Mapper 查询结果平均 10MB
- 系统高并发请求频繁更新缓存
结果就是:
30 × 10MB = 300MB(仅缓存数据)
再加上:
- Tomcat 线程池 1000
- MyBatis 一级缓存临时对象
- 序列化反射对象占用
最终导致:
JVM 内存暴涨 → GC 疯狂打转 → Full GC 卡顿 → Pod 被 OOMKilled!
四、生产环境最佳配置方案(直接抄)
为了防止缓存导致内存爆炸,提议生产环境配置:
mybatis:
configuration:
cache-enabled: false # ❌ 关闭二级缓存
local-cache-scope: statement # ✅ 仅在当前语句级缓存
aggressive-lazy-loading: false # 禁用激进懒加载
lazy-loading-enabled: true # 按需加载
default-statement-timeout: 20 # 超时保护
default-fetch-size: 400 # 限制结果集大小
优点:
✅ 内存占用大幅下降
✅ GC 频率下降 60%+
✅ 查询数据始终最新
✅ Pod 稳定性显著提升
五、搭配高并发优化策略更稳!
光关缓存还不够,这些配置一起上:
⚙️ Hikari 连接池优化
spring:
datasource:
hikari:
maximum-pool-size: 50
minimum-idle: 10
max-lifetime: 300000
connection-timeout: 10000
Tomcat 并发调优
server:
tomcat:
max-threads: 500
accept-count: 500
JVM 参数优化
-Xms512m -Xmx1024m -XX:+UseG1GC -XX:MaxGCPauseMillis=200
SQL 性能监控
开启 MyBatis SQL 日志或接入 Prometheus Exporter 监控慢查询与命中率。
六、总结对比表
|
缓存类型 |
默认状态 |
是否推荐生产开启 |
缘由 |
|
一级缓存 |
✅ 开启 |
✅ 推荐 |
轻量、线程内安全 |
|
二级缓存 |
✅ 开启 |
❌ 禁用 |
内存占用高、无过期机制 |
七、一句话总结
“MyBatis 的二级缓存默认是开的。
在高并发 Kubernetes 环境里,它不是加速器,而是内存炸弹。”





