MyBatis 性能问题大多不是“框架慢”,而是 SQL 次数、扫描行数、返回数据量或事务边界失控。插件适合观测和统一约束,不适合偷偷改写任意业务 SQL。
Table of contents
Open Table of contents
写一个最小插件
SqlCountingPlugin.java下载拦截 StatementHandler.prepare,统计实际准备 Statement 的次数:
@Intercepts(@Signature(
type = StatementHandler.class,
method = "prepare",
args = {Connection.class, Integer.class}))
final class SqlCountingPlugin implements Interceptor {
public Object intercept(Invocation call) throws Throwable {
count.incrementAndGet();
return call.proceed();
}
}
生产插件要限制日志长度、隐藏手机号和密码,并记录 MappedStatement ID、耗时、结果状态,而不是拼出带敏感值的完整 SQL。插件增加在热路径上,必须有开销基准和故障降级。
批处理不是循环加速开关
mvn -Dtest=DataArchitectureLabTest#batchNeedsFlushAndCommit test
ExecutorType.BATCH 会积累参数并在 flushStatements() 或提交时发送。返回值在刷新前不代表数据库最终影响行数;批次太大会占用客户端和服务端内存;中途一条失败时,还要判断哪些语句已执行。因此要限制每批大小、在明确事务中提交,并记录失败位置。
能用一条集合 SQL 表达时,通常优先一条 SQL。例如批量更新状态可考虑临时表或按主键分组,而不是盲目循环 10 万次 Mapper 调用。
三类常见陷阱
- N+1 查询:列表一次,关联对象逐行一次。用 SQL 次数测试、Join 或分批
IN查询治理。 - 动态 SQL 膨胀:大量可选条件使 SQL 形状爆炸,执行计划和测试难稳定。限制查询能力并为常用形状建索引。
- 缓存误用:把一级缓存当跨请求缓存,或开启二级缓存却没有失效模型。事务内一致不等于全局一致。
另一个安全边界是排序字段:列名不能用 #{} 绑定。应把用户输入映射到后端白名单,而不是直接放入 ${orderBy}。
下一步
继续阅读06-04 MySQL 架构与 SQL 生命周期,把视角从 Java 客户端移到数据库服务器。