慢查询日志分析:如何快速定位批量接口的隐藏SQL
批量接口的性能问题,最让人头疼的不是“找不到慢 SQL”,而是“明明接口很慢,慢查询日志里却风平浪静”。你盯着 `long_query_time = 1s` 的日志,发现最大的 SQL 也就 200ms,但接口 P99 已经飙到 3s。这时候,真正的元凶往往藏在批量逻辑里:一条看起来不慢的 SQL,被循环调用了上千次。
为什么批量接口的 SQL 容易“隐身”
慢查询日志的判定逻辑很直接:单条 SQL 执行时间超过阈值才记录。但批量接口的典型模式是“多次小额消费”——比如一次查 500 个商品,代码里 for 循环逐个查库存、查价格、查用户信息。每次查询可能只要 5ms,远低于 1s 阈值,所以一条都不会进慢查询日志。可 500 次乘以 5ms,再加上网络往返和连接池等待,接口耗时轻松突破秒级。
更麻烦的是,这类 SQL 往往长得一模一样,只是参数不同。慢查询日志即使因为某些原因记录了几条,也容易被海量相似记录淹没。再加上连接池复用、事务包裹,想靠肉眼从日志里捞出它们,无异于大海捞针。
让慢查询日志“说话”:先做好采集配置
想定位隐藏 SQL,第一步是让日志留下足够线索。MySQL 8.0 建议开启 `log_slow_extra = ON`,这样慢日志里会带上线程 ID、用户、Host、执行计划等额外字段。阈值不要死守 1s,对于批量接口场景,可以临时调到 100ms 甚至 50ms,先抓出“嫌疑犯”。
如果担心日志量爆炸,可以配合 `pt-query-digest` 做聚合分析。它会把参数不同的同类 SQL 归并成一个“指纹”,然后按总耗时、执行次数、平均耗时排序。这时候你关注的不是“最慢的一条”,而是“总耗时最高、次数最多”的那一类。批量接口的隐藏 SQL,通常就藏在 Count 极大、Avg 很小的条目里。
另一个利器是 `performance_schema.events_statements_summary_by_digest`。它不依赖慢查询阈值,直接统计所有 SQL 指纹的总延迟、执行次数、锁等待时间。查一下 `SUM_TIMER_WAIT` 排名,批量接口的循环 SQL 立刻现形。
三步定位法:从现象到具体 SQL
第一步,按指纹聚合找异常。 用 `pt-query-digest slow.log` 输出报告,重点看 `Response time` 和 `Calls` 两列。如果某个指纹 Calls 是几千次,Avg 只有几毫秒,但总耗时占比很高,基本可以锁定为循环调用。
第二步,把接口日志和 SQL 关联起来。 推荐在 SQL 注释里注入 traceId,例如 `/ trace_id=abc123 / SELECT ...`。这样慢查询日志、general log 里都能直接搜到对应请求。没有 traceId 时,可以临时开启 `general_log`,抓取短时间内该接口的所有 SQL,观察是否有大量重复模板。
第三步,复现并验证。 拿到指纹后,用 `EXPLAIN` 看执行计划,再结合代码确认是否在循环中调用。如果是 ORM 框架,检查是否存在 N+1 查询;如果是 MyBatis,看看是不是在 foreach 里逐条执行而不是批量提交。
一个典型场景:N+1 查询
某订单列表接口,返回 100 条订单,每条订单需要查用户昵称。代码里先查订单列表,然后 for 循环调用 `userMapper.selectById(order.getUserId())`。单条查询 3ms,100 次就是 300ms,加上连接池竞争,实际可能到 800ms。慢查询日志阈值 1s,一条都没记录。用 `performance_schema` 一看,`SELECT * FROM user WHERE id = ?` 这个指纹执行了 100 次,总耗时 400ms,排名第一。优化成 `SELECT * FROM user WHERE id IN (...)` 一次批量查询后,接口耗时直接降到 80ms。
优化与预防:别让隐藏 SQL 再出现
定位只是手段,预防才是目的。批量接口的 SQL 优化原则很明确:能合并就合并,能批量就批量。查询用 `IN` 或 `JOIN` 替代循环单查;插入用 `INSERT INTO ... VALUES (...), (...)` 或 `LOAD DATA`;更新用 `CASE WHEN` 或临时表关联。ORM 层开启预加载、批量抓取,MyBatis 用 `foreach` 拼批量语句,避免在循环里访问数据库。
监控上,定期用 `pt-query-digest` 分析慢日志,结合 APM 的接口追踪,把“接口耗时”和“SQL 总耗时”放在一起看。当接口耗时远大于单条 SQL 耗时,而慢查询日志又没明显记录时,就该怀疑批量循环了。
慢查询日志不是万能的,它只记录“单条慢”,不记录“累计慢”。真正高效的定位方式,是把慢查询日志、performance_schema、接口链路追踪三者结合,从“总耗时”和“执行次数”两个维度去挖掘。批量接口的隐藏 SQL,往往就藏在那些单条不起眼、次数却高得离谱的指纹里。找到它,合并它,接口性能自然就回来了。
转载请注明出处,版权归原作者所有。
管理员
黑卡会员



