MySQL慢查询日志找到那些偷偷变慢的SQL目录慢查询日志是什么开启慢查询日志慢查询日志长什么样mysqldumpslow日志分析工具配合 EXPLAIN 做深度分析常见问题排查清单小结慢查询日志是什么慢查询日志是 MySQL 提供的一个功能它会把执行时间超过指定阈值的 SQL 语句记录到文件里。有了慢查询日志你就不用一行一行翻代码去猜哪条 SQL查询较慢可能存在问题直接看日志就可以找到问题SQL。MySQL 默认是关闭慢查询日志的因为记录日志本身有性能开销所以生产环境通常只在排查问题时临时开启或者设置一个相对严格的阈值比如 1 秒只记录真正有问题的 SQL。开启慢查询日志查看当前状态-- 查看慢查询日志是否开启SHOWVARIABLESLIKEslow_query_log;-- 查看阈值单位秒SHOWVARIABLESLIKElong_query_time;-- 查看日志文件路径SHOWVARIABLESLIKEslow_query_log_file;正常情况下slow_query_log的值是OFFlong_query_time默认是 10 秒。10 秒太宽松了生产环境一般设成 1 秒甚至 0.5 秒主要还是取决于业务场景。临时开启当前会话生效-- 开启慢查询日志SETGLOBALslow_query_logON;-- 设置阈值为 1 秒超过 1 秒的 SQL 才记录SETGLOBALlong_query_time1;-- 可选记录没有使用索引的 SQLSETGLOBALlog_queries_not_using_indexesON;用SET GLOBAL设置的参数在 MySQL 重启后会失效下次启动还是用配置文件里的值。永久开启修改配置文件编辑 MySQL 的配置文件my.cnfLinux或my.iniWindows在[mysqld]段落下添加[mysqld] # 开启慢查询日志 slow_query_log 1 # 慢查询阈值1 秒 long_query_time 1 # 日志文件路径可以自定义 slow_query_log_file /var/log/mysql/slow.log # 可选记录没有使用索引的 SQL log_queries_not_using_indexes 1修改之后重启 MySQL或者执行SET GLOBAL命令让配置立即生效。一个小技巧log_queries_not_using_indexes这个参数很实用。它会把所有没走索引的 SQL 都记录下来不管执行时间多长。这类 SQL 在数据量小的时候可能跑得很快但随着数据增长会越来越慢属于定时炸弹有了这个功能就可以早点发现早点处理。慢查询日志长什么样开启之后我们来制造一条慢查询看看日志长什么样。先准备一张测试表和一些数据CREATETABLEuser_order(idBIGINTPRIMARYKEYAUTO_INCREMENT,user_idBIGINTNOTNULL,order_noVARCHAR(32)NOTNULL,amountDECIMAL(10,2),statusVARCHAR(20),create_timeDATETIME,INDEXidx_user_id(user_id))ENGINEInnoDB;-- 插入 100 万条测试数据DELIMITER//CREATEPROCEDUREgenerate_orders(INnINT)BEGINDECLAREiINTDEFAULT0;WHILEinDOINSERTINTOuser_order(user_id,order_no,amount,status,create_time)VALUES(FLOOR(RAND()*10000),CONCAT(ORD,LPAD(i,10,0)),ROUND(RAND()*1000,2),ELT(FLOOR(RAND()*3)1,pending,paid,cancelled),DATE_SUB(NOW(),INTERVALFLOOR(RAND()*365)DAY));SETii1;ENDWHILE;END//DELIMITER;CALLgenerate_orders(1000000);现在执行一条故意不走索引的查询-- 对 status 做模糊查询status 上没有索引SELECT*FROMuser_orderWHEREstatusLIKEp%ORDERBYcreate_timeDESCLIMIT50;等它跑完可能要几秒然后去日志文件里找找看。日志文件的路径可以用SHOW VARIABLES LIKE slow_query_log_file查看。一条典型的慢查询日志长这样# Time: 2026-06-21T14:32:05.12345608:00 # UserHost: root[root] localhost [] Id: 42 # Query_time: 2.356789 Lock_time: 0.000123 Rows_sent: 50 Rows_examined: 1000000 SET timestamp1718946725; SELECT * FROM user_order WHERE status LIKE p% ORDER BY create_time DESC LIMIT 50;逐行解读字段含义重点关注TimeSQL 执行的时间点定位问题发生的时刻UserHost执行 SQL 的用户和来源排查是不是某个服务在搞事Query_timeSQL 执行总耗时秒核心指标越小越好Lock_time等待锁的时间秒如果很大说明有锁竞争Rows_sent返回给客户端的行数和 LIMIT 对比看有没有多返回Rows_examined扫描的行数核心指标越大说明越低效SQL 语句实际执行的 SQL拿去 EXPLAIN 分析最该关注的两个数字Query_time 和 Rows_examined。Query_time 告诉你这条 SQL 到底慢不慢Rows_examined 告诉你它为什么慢。如果 Rows_examined 是 100 万但 Rows_sent 只有 50说明 MySQL 扫了 100 万行才挑出 50 条——这就是典型的索引缺失或索引失效。mysqldumpslow日志分析工具日志文件看几条还行但如果慢查询很多一条条翻就太低效了。MySQL 自带了一个日志分析工具mysqldumpslow可以帮我们做汇总统计。# 按执行时间排序取前 10 条最慢的mysqldumpslow-st-t10/var/log/mysql/slow.log# 按扫描行数排序取前 10 条mysqldumpslow-sr-t10/var/log/mysql/slow.log# 按执行次数排序取前 10 条mysqldumpslow-sc-t10/var/log/mysql/slow.log参数说明参数含义-s t按总执行时间排序默认-s r按扫描总行数排序-s c按执行次数排序-s l按锁等待时间排序-t N只显示前 N 条-g pattern只匹配包含指定字符串的 SQL输出结果类似Count: 125 Time2.36s (295s) Lock0.00s (0.15s) Rows50.0 (6250), root[root]localhost SELECT * FROM user_order WHERE status LIKE S ORDER BY create_time DESC LIMIT N这一行告诉我们这条 SQL 在统计时段内执行了 125 次平均耗时 2.36 秒累计耗时 295 秒平均扫描行数 6250 条。Count 大的说明是高频 SQLTime 大的说明是耗时大户。如果一条 SQL Count 和 Time 都大那它就是性能优化的第一优先级。配合 EXPLAIN 做深度分析慢查询日志帮你找到了嫌疑人但要定罪还需要充分的证据。所以在拿到日志里的 SQL 后我们继续用EXPLAIN看看它的执行计划EXPLAINSELECT*FROMuser_orderWHEREstatusLIKEp%ORDERBYcreate_timeDESCLIMIT50;输出可能是----------------------------------------------------------------------------- | id | type | key | ref | rows | filtered | Extra | ------------------------------------------------------------------------------ | 1 | ALL | NULL | NULL | 1000000 | 33.33 | Using where; Using filesort | ------------------------------------------------------------------------------四个危险信号type ALL全表扫描没走索引key NULL没有可用索引rows 1000000扫描了 100 万行Extra Using filesort额外排序和日志里的 Rows_examined 1000000 对上了。问题很明确status列没有索引MySQL 只能全表扫描。优化方案给status加索引或者改成联合索引(status, create_time)同时覆盖过滤和排序ALTERTABLEuser_orderADDINDEXidx_status_time(status,create_time);再查一次 EXPLAIN----------------------------------------------------------------- | id | type | key | ref | rows | filtered | Extra | ----------------------------------------------------------------- | 1 | range | idx_status_time| NULL | 333333 | 100.00 | Using where | -----------------------------------------------------------------type 从 ALL 变成了 rangerows 从 100 万降到了 33 万Using filesort 也没了。执行时间从 2 秒多降到几百毫秒。慢查询日志负责发现问题EXPLAIN 负责定位原因两者配合才是完整的调优链路。常见问题排查清单拿到慢查询日志后按照这个清单逐项检查现象可能原因排查方法Rows_examined 远大于 Rows_sent索引缺失或索引失效EXPLAIN 看 type 和 keyQuery_time 大但 Rows_examined 不大锁等待、IO 等待关注 Lock_time检查是否有表锁同一条 SQL 时快时慢执行计划不稳定EXPLAIN 看是否有多个候选索引日志里出现大量相同 SQL慢查询集中在某几张表mysqldumpslow -s c 统计频次某个时间段集中出现慢查询定时任务、批量导入检查 Time 字段的时间分布Lock_time 很大有事务长时间未提交检查SHOW ENGINE INNODB STATUS一个实用的排查流程小结**慢查询日志是 MySQL 性能排查的起点。**它帮你从海量 SQL 中筛选出真正有问题的那些附上执行时间、扫描行数等关键指标让你的调优工作有的放矢。从实际操作来看慢查询日志 EXPLAIN 是一对黄金搭档。前者负责发现后者负责诊断。发现问题是第一步解决问题是第二步在后端面试之中遇到“mysql如何调优”这个问题我们就可以采用这个思路进行回答。在实际工作生产环境中我们依然使用这个思路进行实际的排查优化。
MySQL慢查询日志:找到那些偷偷变慢的SQL
发布时间:2026/6/22 1:12:07
MySQL慢查询日志找到那些偷偷变慢的SQL目录慢查询日志是什么开启慢查询日志慢查询日志长什么样mysqldumpslow日志分析工具配合 EXPLAIN 做深度分析常见问题排查清单小结慢查询日志是什么慢查询日志是 MySQL 提供的一个功能它会把执行时间超过指定阈值的 SQL 语句记录到文件里。有了慢查询日志你就不用一行一行翻代码去猜哪条 SQL查询较慢可能存在问题直接看日志就可以找到问题SQL。MySQL 默认是关闭慢查询日志的因为记录日志本身有性能开销所以生产环境通常只在排查问题时临时开启或者设置一个相对严格的阈值比如 1 秒只记录真正有问题的 SQL。开启慢查询日志查看当前状态-- 查看慢查询日志是否开启SHOWVARIABLESLIKEslow_query_log;-- 查看阈值单位秒SHOWVARIABLESLIKElong_query_time;-- 查看日志文件路径SHOWVARIABLESLIKEslow_query_log_file;正常情况下slow_query_log的值是OFFlong_query_time默认是 10 秒。10 秒太宽松了生产环境一般设成 1 秒甚至 0.5 秒主要还是取决于业务场景。临时开启当前会话生效-- 开启慢查询日志SETGLOBALslow_query_logON;-- 设置阈值为 1 秒超过 1 秒的 SQL 才记录SETGLOBALlong_query_time1;-- 可选记录没有使用索引的 SQLSETGLOBALlog_queries_not_using_indexesON;用SET GLOBAL设置的参数在 MySQL 重启后会失效下次启动还是用配置文件里的值。永久开启修改配置文件编辑 MySQL 的配置文件my.cnfLinux或my.iniWindows在[mysqld]段落下添加[mysqld] # 开启慢查询日志 slow_query_log 1 # 慢查询阈值1 秒 long_query_time 1 # 日志文件路径可以自定义 slow_query_log_file /var/log/mysql/slow.log # 可选记录没有使用索引的 SQL log_queries_not_using_indexes 1修改之后重启 MySQL或者执行SET GLOBAL命令让配置立即生效。一个小技巧log_queries_not_using_indexes这个参数很实用。它会把所有没走索引的 SQL 都记录下来不管执行时间多长。这类 SQL 在数据量小的时候可能跑得很快但随着数据增长会越来越慢属于定时炸弹有了这个功能就可以早点发现早点处理。慢查询日志长什么样开启之后我们来制造一条慢查询看看日志长什么样。先准备一张测试表和一些数据CREATETABLEuser_order(idBIGINTPRIMARYKEYAUTO_INCREMENT,user_idBIGINTNOTNULL,order_noVARCHAR(32)NOTNULL,amountDECIMAL(10,2),statusVARCHAR(20),create_timeDATETIME,INDEXidx_user_id(user_id))ENGINEInnoDB;-- 插入 100 万条测试数据DELIMITER//CREATEPROCEDUREgenerate_orders(INnINT)BEGINDECLAREiINTDEFAULT0;WHILEinDOINSERTINTOuser_order(user_id,order_no,amount,status,create_time)VALUES(FLOOR(RAND()*10000),CONCAT(ORD,LPAD(i,10,0)),ROUND(RAND()*1000,2),ELT(FLOOR(RAND()*3)1,pending,paid,cancelled),DATE_SUB(NOW(),INTERVALFLOOR(RAND()*365)DAY));SETii1;ENDWHILE;END//DELIMITER;CALLgenerate_orders(1000000);现在执行一条故意不走索引的查询-- 对 status 做模糊查询status 上没有索引SELECT*FROMuser_orderWHEREstatusLIKEp%ORDERBYcreate_timeDESCLIMIT50;等它跑完可能要几秒然后去日志文件里找找看。日志文件的路径可以用SHOW VARIABLES LIKE slow_query_log_file查看。一条典型的慢查询日志长这样# Time: 2026-06-21T14:32:05.12345608:00 # UserHost: root[root] localhost [] Id: 42 # Query_time: 2.356789 Lock_time: 0.000123 Rows_sent: 50 Rows_examined: 1000000 SET timestamp1718946725; SELECT * FROM user_order WHERE status LIKE p% ORDER BY create_time DESC LIMIT 50;逐行解读字段含义重点关注TimeSQL 执行的时间点定位问题发生的时刻UserHost执行 SQL 的用户和来源排查是不是某个服务在搞事Query_timeSQL 执行总耗时秒核心指标越小越好Lock_time等待锁的时间秒如果很大说明有锁竞争Rows_sent返回给客户端的行数和 LIMIT 对比看有没有多返回Rows_examined扫描的行数核心指标越大说明越低效SQL 语句实际执行的 SQL拿去 EXPLAIN 分析最该关注的两个数字Query_time 和 Rows_examined。Query_time 告诉你这条 SQL 到底慢不慢Rows_examined 告诉你它为什么慢。如果 Rows_examined 是 100 万但 Rows_sent 只有 50说明 MySQL 扫了 100 万行才挑出 50 条——这就是典型的索引缺失或索引失效。mysqldumpslow日志分析工具日志文件看几条还行但如果慢查询很多一条条翻就太低效了。MySQL 自带了一个日志分析工具mysqldumpslow可以帮我们做汇总统计。# 按执行时间排序取前 10 条最慢的mysqldumpslow-st-t10/var/log/mysql/slow.log# 按扫描行数排序取前 10 条mysqldumpslow-sr-t10/var/log/mysql/slow.log# 按执行次数排序取前 10 条mysqldumpslow-sc-t10/var/log/mysql/slow.log参数说明参数含义-s t按总执行时间排序默认-s r按扫描总行数排序-s c按执行次数排序-s l按锁等待时间排序-t N只显示前 N 条-g pattern只匹配包含指定字符串的 SQL输出结果类似Count: 125 Time2.36s (295s) Lock0.00s (0.15s) Rows50.0 (6250), root[root]localhost SELECT * FROM user_order WHERE status LIKE S ORDER BY create_time DESC LIMIT N这一行告诉我们这条 SQL 在统计时段内执行了 125 次平均耗时 2.36 秒累计耗时 295 秒平均扫描行数 6250 条。Count 大的说明是高频 SQLTime 大的说明是耗时大户。如果一条 SQL Count 和 Time 都大那它就是性能优化的第一优先级。配合 EXPLAIN 做深度分析慢查询日志帮你找到了嫌疑人但要定罪还需要充分的证据。所以在拿到日志里的 SQL 后我们继续用EXPLAIN看看它的执行计划EXPLAINSELECT*FROMuser_orderWHEREstatusLIKEp%ORDERBYcreate_timeDESCLIMIT50;输出可能是----------------------------------------------------------------------------- | id | type | key | ref | rows | filtered | Extra | ------------------------------------------------------------------------------ | 1 | ALL | NULL | NULL | 1000000 | 33.33 | Using where; Using filesort | ------------------------------------------------------------------------------四个危险信号type ALL全表扫描没走索引key NULL没有可用索引rows 1000000扫描了 100 万行Extra Using filesort额外排序和日志里的 Rows_examined 1000000 对上了。问题很明确status列没有索引MySQL 只能全表扫描。优化方案给status加索引或者改成联合索引(status, create_time)同时覆盖过滤和排序ALTERTABLEuser_orderADDINDEXidx_status_time(status,create_time);再查一次 EXPLAIN----------------------------------------------------------------- | id | type | key | ref | rows | filtered | Extra | ----------------------------------------------------------------- | 1 | range | idx_status_time| NULL | 333333 | 100.00 | Using where | -----------------------------------------------------------------type 从 ALL 变成了 rangerows 从 100 万降到了 33 万Using filesort 也没了。执行时间从 2 秒多降到几百毫秒。慢查询日志负责发现问题EXPLAIN 负责定位原因两者配合才是完整的调优链路。常见问题排查清单拿到慢查询日志后按照这个清单逐项检查现象可能原因排查方法Rows_examined 远大于 Rows_sent索引缺失或索引失效EXPLAIN 看 type 和 keyQuery_time 大但 Rows_examined 不大锁等待、IO 等待关注 Lock_time检查是否有表锁同一条 SQL 时快时慢执行计划不稳定EXPLAIN 看是否有多个候选索引日志里出现大量相同 SQL慢查询集中在某几张表mysqldumpslow -s c 统计频次某个时间段集中出现慢查询定时任务、批量导入检查 Time 字段的时间分布Lock_time 很大有事务长时间未提交检查SHOW ENGINE INNODB STATUS一个实用的排查流程小结**慢查询日志是 MySQL 性能排查的起点。**它帮你从海量 SQL 中筛选出真正有问题的那些附上执行时间、扫描行数等关键指标让你的调优工作有的放矢。从实际操作来看慢查询日志 EXPLAIN 是一对黄金搭档。前者负责发现后者负责诊断。发现问题是第一步解决问题是第二步在后端面试之中遇到“mysql如何调优”这个问题我们就可以采用这个思路进行回答。在实际工作生产环境中我们依然使用这个思路进行实际的排查优化。