性能排查:SQL 日志、慢查询定位与批处理

接口慢了先别猜,链路是:看到真实 SQL → 找到慢在哪 → 用索引/批处理解决。MyBatis 场景里对应三步:打 SQL 日志、查慢查询、开批处理。

一、让 MyBatis 把 SQL 打出来

日志实现可全局配置(logImpl 支持 SLF4J、LOG4J2、STDOUT_LOGGING 等,未配置时按 classpath 自动发现,以官方文档为准):

<settings>
    <setting name="logImpl" value="SLF4J"/>
</settings>

Spring Boot 更常用的两种方式:

mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台直出
logging:
  level:
    com.demo.mapper: debug    # 或只把 Mapper 包调到 debug

典型输出三段:Preparing: SELECT * FROM user WHERE id = ?Parameters: 1(String)Total: 1。Preparing 就是真实执行的 SQL,直接复制去做 EXPLAIN。

二、慢查询定位

数据库侧先把慢 SQL 捞出来:MySQL 开 slow_query_log、设 long_query_time,日志里就是候选优化对象(配置以 MySQL 官方文档为准)。拿到慢 SQL 后用 EXPLAIN 分析:

EXPLAIN SELECT u.id, u.name, COUNT(o.id)
FROM user u LEFT JOIN `order` o ON o.user_id = u.id
WHERE u.status = 1 GROUP BY u.id;

重点看四样:type(出现 ALL 即全表扫,应到 range/ref/eq_ref)、key(是否走索引)、rows(扫描行数)、Extra(Using filesort / temporary 是优化信号)。缺索引就补,复合索引要结合 WHERE 与 ORDER BY 一起设计(细节以数据库官方文档为准)。

三、批处理

循环单条 insert 是"慢"重灾区,每条 SQL 都一次网络往返。MyBatis 提供批量执行器:

// 原生 API:批量执行器
try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
    UserMapper mapper = session.getMapper(UserMapper.class);
    for (User u : users) {
        mapper.insert(u);   // 先攒在内存,不逐条提交
    }
    session.commit();       // flushStatements 统一发出
}

Spring 中换执行器:new SqlSessionTemplate(sqlSessionFactory, ExecutorType.BATCH)。三个注意点:

  • 同一批次内先插后查要小心,BATCH 模式下查询会先 flush 缓存,批量 id 回填行为以驱动为准;
  • update 返回的影响行数在真正 flush 前不可靠,别拿它做业务判断;
  • 攒到一定条数主动 flushStatements 一次,防止内存与单条 SQL 过大。

最后记得 MySQL 驱动侧开 rewriteBatchedStatements=true(JDBC URL 参数),否则"假批处理"仍是一条条发。MyBatis-Plus 的 saveBatch 底层同样依赖批处理执行器与驱动支持(以官方文档为准)。

排查口诀:日志里抄 SQL → EXPLAIN 看索引 → 循环写改批处理。慢的不是框架,是"看不见的 SQL 和一条条发的 INSERT",把这三步做成习惯即可。

笔记加载中…