慢SQL拖垮整体指标,答案不是看数据库一个面,而是把慢日志和业务调用链日志对齐到同一时间线上,交叉比对方能真正定位根因。很多团队排查慢SQL,只看慢日志里哪条语句执行时间长,却没想过这个时间段里前端、应用层、Redis、消息队列在干什么,整体指标恶化往往是连锁反应,SQL只是链条上的一环。
慢SQL拖垮整体指标怎么查?先理解它是怎么连锁爆发的
慢SQL不会孤立存在,一条执行三秒的UPDATE,它影响的绝不只自己,数据库连接池被它占着,后面的线程都在排队,应用服务器的线程池也被拖满,接口RT飙升,网关开始重试,重试又加深数据库压力,最终整体可用率崩掉。
这个连锁反应在日志里是有痕迹的,只是分布在不同系统里,行业共识认为,排查这类问题的关键不是把SQL揪出来就完事,而是要把多个日志源的同一个时间切片拼在一起,还原那个时间窗口里到底发生了什么。
要从日志层面找到那条“肇事”语句,常规链路是这三步:
- 先在数据库侧开启慢查询日志,确认慢SQL的准确执行时间和执行计划
- 再去业务日志里按同一个时间点,找出对应的traceId或请求日志,看应用在等什么
- 最后把两侧日志的Timeout、等待时间、返回行数拼起来,确认SQL慢是主因还是背锅
慢日志分析怎么做?从原始文件到可疑语句的完整路径
很多开发把慢日志打开了就不管,这是最常见的问题,慢日志文件只是堆积数据,不解析等于零。
第一步:确认慢查询日志真的开着
先看当前配置:
SHOW VARIABLES LIKE 'slow_query_log'; SHOW VARIABLES LIKE 'long_query_time'; SHOW VARIABLES LIKE 'slow_query_log_file';
如果slow_query_log是OFF,线上临时开启可以,但要注意文件体积增长,建议配合log_queries_not_using_indexes一起打开,这样没走索引的语句也会被记录,不过某些大表全表扫描频率高,日志会暴涨,控制好阈值。

long_query_time建议设成1秒或更低,业务高峰期可以临时调成0.5秒,太松抓不到有问题的语句,太紧日志噪音太大。
第二步:用工具解析慢日志,别靠肉眼翻文件
慢日志里的原始记录长这样,包括Query_time、Lock_time、Rows_examined、Rows_sent,这一步的核心是从几百条记录里找出真正有问题的语句。
推荐用pt-query-digest做离线分析,一条命令就能按“总耗时”和“平均耗时”排序:
pt-query-digest /var/lib/mysql/mysql-slow.log > /tmp/slow_report.txt
也可以直接在MySQL里统计:
SELECT FROM mysql.slow_log ORDER BY query_time DESC LIMIT 20;
解析之后,重点看两个维度:总执行次数和单次最大耗时,总执行次数多的慢语句,即使单条不快也是隐性杀手;单次很慢但次数少的,多半是偶发大批量扫描。
第三步:用EXPLAIN验证执行计划
慢日志只告诉你“它慢”,不告诉你“为什么慢”,拿解析出的SQL去跑EXPLAIN:
EXPLAIN SELECT FROM orders WHERE user_id = 12345;
重点检查type列是不是ALL或index,rows列是不是远高于预期,Extra里有没有Using filesort或Using temporary,这些都指向索引失效或者查询条件写得不对。
慢日志和业务日志怎么联合排查?按时间线还原现场
先拉出同一时间窗口的业务日志片段
慢SQL执行时间是10:23:31,这时候你要去应用日志里找10:23:25到10:23:40之间跟这个用户、这个订单、这个接口相关的所有请求,重点找几个信号:
- 应用层日志里有没有“timeout”或“connection reset”异常
- 业务日志里出现多次重试的traceId,时间戳是不是恰好落在慢SQL执行前后
- 接口的RT日志在那一分钟里有没有集体飙升

如果好几百个接口都在同一秒变慢,慢SQL只是其中一条,说明数据库有锁等待或连接池被打满,SQL是被拖累的;如果只有那一个接口慢,其它接口正常,那这条SQL大概率是真凶。
再做调用链日志的穿透
有调用链系统的话,用traceId在链路日志里搜,看这个请求在MySQL这个节点上花了多久,在Redis和下游RPC上各花了多久,假设MySQL耗时2000ms,Redis耗时3ms,下游耗时50ms,那瓶颈非常明确就在数据库那一环。
没有调用链系统的话,把应用日志里打印的SQL耗时字段搜出来,按耗时排序,用业务日志里的timestamp做对齐,注意服务器时钟要一致,不然后面全是白干,跨机房的情况,先做时钟校准再排查。
结合数据库指标日志找旁证
慢SQL出现的时间段,数据库的状态变量也会有明显变化,可以在同一时间窗口内查:
- Threads_connected是否远超平时,连接数被占满
- Innodb_row_lock_current_waits是否大于零,有行锁争用
- QPS和TPS有没有跳水或断崖
这些数据可以通过Percona Monitoring(PMM)或者zabbix回顾历史图表,不解释原因,但能验证:慢SQL发生那几十秒里,数据库确实是压力爆表的状态,不是应用层造成的误报。
慢SQL排查的常见误区:别把背锅的当成元凶
Rows_examined很大就等于SQL写得烂
一条SQL扫描一百万行,也可能走的是正确索引,真正要判断的是扫描行数和返回行数的比值。扫描一百万行返回一条,八成是索引选择出了问题;扫描一百万行返回八十万,说明本来就要捞这么多数据,问题可能出在查询需求层面。
慢日志里没有记录,就断定SQL没问题
要分清慢日志没记录和慢日志没开启,很多线上数据库慢日志设了long_query_time=10秒,应用端500ms就超时报错,慢日志里自然什么都没有,先确认“没记录”是没达到阈值,还是真没有。

只看SQL本身,忽略锁等待时间
一条UPDATE的Query_time=5秒,其中Lock_time占了4.8秒,真实问题是别的地方锁着行没释放,SQL本身执行只要200ms,联合锁等待日志和innodb status一起看,才有结论。
联合日志查完之后的落地排查步骤
日志联合定位只能定位到方向和语句级别,接下来再往下钻,层层验证:
- 用performance_schema看这条SQL在事件级别的耗时分布,把时间花在等待I/O还是等待CPU算出来
- 用sys.schema_table_lock_waits查有没有表锁在排队
- 看SHOW ENGINE INNODB STATUS里面的LATEST DETECTED DEADLOCK区块,确认有没有死锁回滚
- 如果频繁出现,把锁等待和慢日志的时间匹配上,就能确认是锁等待型慢SQL
这时再根据情况给出结论是缺索引、覆盖索引不够、查询条件写错、还是大事务没提交,一个原则:别直接上线加索引,先在测试环境用同一份数据和执行计划复现一遍。
关于慢日志排查后续治理的补充问题
Q:开启慢查询日志会影响数据库性能吗?
开启慢日志本身对性能影响有限,但日志落盘量过大时会有I/O压力,线上建议把slow_query_log_file放到独立的磁盘分区,或者设置log_output=TABLE对特殊场景做临时采集,使用完毕后记得改回原配置。
Q:慢SQL整体指标正常了,但MySQL的CPU偶尔还会飙到90%,怎么继续查?
慢日志里查不到,就把视野放宽,查全量日志里这一时间窗口的活跃语句,用SHOW FULL PROCESSLIST多抓几次快照,按Time倒序看有没有没达到慢日志阈值但重复执行次数超高的语句,还有一句提醒:别只看执行时间,把Rows_sent特别大的SELECT也纳入排查范围,即使它秒回,也有可能瞬间拉满网络层的带宽导致整体指标恶化。