MySQL慢查询日志:找到那些偷偷变慢的SQL
2026/6/22 1:12:00 网站建设 项目流程

MySQL慢查询日志:找到那些偷偷变慢的SQL

目录

  • 慢查询日志是什么
  • 开启慢查询日志
  • 慢查询日志长什么样
  • mysqldumpslow:日志分析工具
  • 配合 EXPLAIN 做深度分析
  • 常见问题排查清单
  • 小结

慢查询日志是什么

慢查询日志是 MySQL 提供的一个功能,它会把执行时间超过指定阈值的 SQL 语句记录到文件里。

有了慢查询日志,你就不用一行一行翻代码去猜哪条 SQL查询较慢可能存在问题,直接看日志就可以找到问题SQL。

MySQL 默认是关闭慢查询日志的,因为记录日志本身有性能开销,所以生产环境通常只在排查问题时临时开启,或者设置一个相对严格的阈值(比如 1 秒),只记录真正有问题的 SQL。

开启慢查询日志

查看当前状态

-- 查看慢查询日志是否开启SHOWVARIABLESLIKE'slow_query_log';-- 查看阈值(单位:秒)SHOWVARIABLESLIKE'long_query_time';-- 查看日志文件路径SHOWVARIABLESLIKE'slow_query_log_file';

正常情况下,slow_query_log的值是OFFlong_query_time默认是 10 秒。10 秒太宽松了,生产环境一般设成 1 秒甚至 0.5 秒,主要还是取决于业务场景。

临时开启(当前会话生效)

-- 开启慢查询日志SETGLOBALslow_query_log='ON';-- 设置阈值为 1 秒(超过 1 秒的 SQL 才记录)SETGLOBALlong_query_time=1;-- 可选:记录没有使用索引的 SQLSETGLOBALlog_queries_not_using_indexes='ON';

SET GLOBAL设置的参数在 MySQL 重启后会失效,下次启动还是用配置文件里的值。

永久开启(修改配置文件)

编辑 MySQL 的配置文件my.cnf(Linux)或my.ini(Windows),在[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))ENGINE=InnoDB;-- 插入 100 万条测试数据DELIMITER//CREATEPROCEDUREgenerate_orders(INnINT)BEGINDECLAREiINTDEFAULT0;WHILEi<nDOINSERTINTOuser_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));SETi=i+1;ENDWHILE;END//DELIMITER;CALLgenerate_orders(1000000);

现在执行一条故意不走索引的查询:

-- 对 status 做模糊查询,status 上没有索引SELECT*FROMuser_orderWHEREstatusLIKE'p%'ORDERBYcreate_timeDESCLIMIT50;

等它跑完(可能要几秒),然后去日志文件里找找看。日志文件的路径可以用SHOW VARIABLES LIKE 'slow_query_log_file'查看。

一条典型的慢查询日志长这样:

# Time: 2026-06-21T14:32:05.123456+08:00 # User@Host: root[root] @ localhost [] Id: 42 # Query_time: 2.356789 Lock_time: 0.000123 Rows_sent: 50 Rows_examined: 1000000 SET timestamp=1718946725; SELECT * FROM user_order WHERE status LIKE 'p%' ORDER BY create_time DESC LIMIT 50;

逐行解读:

字段含义重点关注
TimeSQL 执行的时间点定位问题发生的时刻
User@Host执行 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 Time=2.36s (295s) Lock=0.00s (0.15s) Rows=50.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 大的说明是高频 SQL,Time 大的说明是耗时大户。如果一条 SQL Count 和 Time 都大,那它就是性能优化的第一优先级。

配合 EXPLAIN 做深度分析

慢查询日志帮你找到了"嫌疑人",但要定罪还需要充分的证据。所以在拿到日志里的 SQL 后,我们继续用EXPLAIN看看它的执行计划:

EXPLAINSELECT*FROMuser_orderWHEREstatusLIKE'p%'ORDERBYcreate_timeDESCLIMIT50;

输出可能是:

+----+------+-------+------+---------+------+----------+-----------------------------+ | id | type | key | ref | rows | filtered | Extra | +----+------+-------+------+---------+----------+------------------------------------+ | 1 | ALL | NULL | NULL | 1000000 | 33.33 | Using where; Using filesort | +----+------+-------+------+---------+----------+------------------------------------+

四个危险信号:

  1. type = ALL:全表扫描,没走索引
  2. key = NULL:没有可用索引
  3. rows = 1000000:扫描了 100 万行
  4. 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 变成了 range,rows 从 100 万降到了 33 万,Using filesort 也没了。执行时间从 2 秒多降到几百毫秒。

慢查询日志负责"发现问题",EXPLAIN 负责"定位原因",两者配合才是完整的调优链路。

常见问题排查清单

拿到慢查询日志后,按照这个清单逐项检查:

现象可能原因排查方法
Rows_examined 远大于 Rows_sent索引缺失或索引失效EXPLAIN 看 type 和 key
Query_time 大但 Rows_examined 不大锁等待、IO 等待关注 Lock_time,检查是否有表锁
同一条 SQL 时快时慢执行计划不稳定EXPLAIN 看是否有多个候选索引
日志里出现大量相同 SQL慢查询集中在某几张表mysqldumpslow -s c 统计频次
某个时间段集中出现慢查询定时任务、批量导入检查 Time 字段的时间分布
Lock_time 很大有事务长时间未提交检查SHOW ENGINE INNODB STATUS

一个实用的排查流程:

小结

**慢查询日志是 MySQL 性能排查的起点。**它帮你从海量 SQL 中筛选出真正有问题的那些,附上执行时间、扫描行数等关键指标,让你的调优工作有的放矢。

从实际操作来看,慢查询日志 + EXPLAIN 是一对黄金搭档。前者负责"发现",后者负责"诊断"。发现问题是第一步,解决问题是第二步,在后端面试之中遇到“mysql如何调优”这个问题,我们就可以采用这个思路进行回答。在实际工作生产环境中,我们依然使用这个思路进行实际的排查优化。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询