跳到正文
Elaine Blog
返回

MyBatis 插件、批处理与性能陷阱

MyBatis、MySQL 与数据架构

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 调用。

三类常见陷阱

  1. N+1 查询:列表一次,关联对象逐行一次。用 SQL 次数测试、Join 或分批 IN 查询治理。
  2. 动态 SQL 膨胀:大量可选条件使 SQL 形状爆炸,执行计划和测试难稳定。限制查询能力并为常用形状建索引。
  3. 缓存误用:把一级缓存当跨请求缓存,或开启二级缓存却没有失效模型。事务内一致不等于全局一致。

另一个安全边界是排序字段:列名不能用 #{} 绑定。应把用户输入映射到后端白名单,而不是直接放入 ${orderBy}

下一步

继续阅读06-04 MySQL 架构与 SQL 生命周期,把视角从 Java 客户端移到数据库服务器。


分享这篇文章:

上一篇
MyBatis SQL 执行、缓存与结果映射
下一篇
MySQL 架构与 SQL 生命周期