导读:本期聚焦于IT柏拉图创作的《MySQL慢查询如何排查?慢日志配置与优化完整指南》,敬请观看详情。一条原本毫秒级返回的SQL,上线后却频繁超时,这类问题通常来自慢查询。慢查询不会自动消失,排查的关键在于先把慢语句记录下来。本文从慢查询日志的参数配置入手,说明如何确定合理的long_query_time、开启未走索引查询记录,再用mysqldumpslow和pt-query-digest对日志做聚合排序,找到执行次数多、平均耗时高的语句。拿到慢SQL后,通过EXPLAIN查看访问类型、可能用到的索引、实际命中的索引以及扫描行数,判断是否走了全表扫描或产生了临时表。文章最后给出索引设计、深分页改写、避免隐式类型转换和JOIN顺序调整等优化方案,帮助读者形成从发现、定位到治理的完整排查路径。

数据库响应变慢时,慢查询日志是最直接的线索来源。很多团队只在出现严重超时后才临时打开日志,缺少持续收集和分析习惯,导致问题反复发生。要系统排查 MySQL 慢查询,应当先保证慢日志配置合理,再借助工具定位问题 SQL,最后结合执行计划与索引设计完成优化。下面围绕这条路径展开。

MySQL慢查询如何排查?慢日志配置与优化完整指南

一、把慢查询日志配置到位

慢查询日志默认可能是关闭的,即便开启,默认的 long_query_time 也可能不符合业务预期。这个参数控制超过多少秒的查询会被记录,线上环境不建议设置为 0,否则所有 SQL 都会写入日志,磁盘和 I/O 很快会被拖垮。一般可以从 1 秒或 2 秒开始,结合业务可接受的延迟逐步收紧。另一个容易被忽略的参数是 log_queries_not_using_indexes,开启后未使用索引的查询也会被记录,但可能产生大量日志,需要配合 min_examined_row_limit 过滤扫描行数极少的查询。

在会话中临时调整可以使用 SET GLOBAL,这样不用重启数据库即可生效,但重启后会失效。持久化配置则需要写入 MySQL 配置文件。下面分别给出两种方式的示例。

SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 2;
SET GLOBAL min_examined_row_limit = 100;
SET GLOBAL log_queries_not_using_indexes = ON;

配置文件方式通常写入 [mysqld] 段,示例如下:

[mysqld]
slow_query_log = 1
slow_query_log_file = /var/lib/mysql/mysql-slow.log
long_query_time = 2
log_queries_not_using_indexes = 1
min_examined_row_limit = 100
log_output = FILE

日志输出方式可以选择 FILE 或 TABLE。写入文件更常见,便于后续用命令行工具分析;写入 mysql.slow_log 表则方便用 SQL 查询,但在高负载下可能增加额外开销。通常建议生产环境使用文件输出,并做好日志轮转,避免单个文件过大影响分析效率。

二、用工具从海量慢日志中提取有效信息

慢日志一大特点是重复度高,同一个模板的 SQL 可能记录成千上万条。直接逐行看文件效率很低,需要先做聚合处理。MySQL 自带 mysqldumpslow 工具,可以按执行次数、总耗时、锁等待时间等维度排序。例如 -s t 表示按平均查询时间排序,-s c 表示按出现次数排序,-t 10 表示只显示前十条。

mysqldumpslow -s t -t 10 /var/lib/mysql/mysql-slow.log

mysqldumpslow 适合快速查看日志,但对 SQL 指纹的归一化比较粗糙,复杂场景建议使用 Percona Toolkit 中的 pt-query-digest。它不仅能分析慢日志文件,还能解析通用日志、二进制日志和 processlist,输出结果包含请求占比、响应时间分布、执行计划示例等,信息量远大于自带工具。基本用法如下:

pt-query-digest /var/lib/mysql/mysql-slow.log

解读输出时,优先关注排名靠前的查询。重点看 Query_time 和 Lock_time,如果锁等待时间占比过高,说明可能不是 SQL 本身的执行慢,而是存在锁竞争。再比较 Rows_examined 与 Rows_sent 的比值,该值过大通常意味着扫描了大量行却只返回少量数据,这是索引缺失或执行计划不合理的典型表现。

三、通过 EXPLAIN 定位执行计划中的问题

找到候选慢 SQL 后,直接把 SQL 放到 EXPLAIN 前面执行。EXPLAIN 不会真正执行查询,只返回优化器选择的执行计划。核心字段包括 type、possible_keys、key、rows 和 Extra。其中 type 从好到差依次为 system、const、eq_ref、ref、range、index、ALL。如果 type 为 ALL,说明发生了全表扫描;key 为 NULL 表示没有实际使用索引;Extra 中出现 Using filesort 或 Using temporary 也提示排序或分组操作消耗了额外资源。

EXPLAIN SELECT id, user_id, order_amount
FROM orders
WHERE user_id = 100;

对应的输出可能如下:

+----+-------------+--------+------+---------------+-------------+---------+-------+------+-------+
| id | select_type | table  | type | possible_keys | key         | key_len | ref   | rows | Extra |
+----+-------------+--------+------+---------------+-------------+---------+-------+------+-------+
|  1 | SIMPLE      | orders | ref  | idx_user_id   | idx_user_id | 8       | const |   12 | NULL  |
+----+-------------+--------+------+---------------+-------------+---------+-------+------+-------+

这里 type=ref 表示使用非唯一索引查找,rows=12 表示估计只会扫描 12 行,Extra 为 NULL 说明没有临时表或文件排序。如果同样是这条 SQL,rows 变成几十万,或者 type=ALL,就需要继续优化。MySQL 8.0.18 及以上版本还可以使用 EXPLAIN ANALYZE,它会实际执行语句并输出每一步的真实耗时,比传统 EXPLAIN 更接近实际场景。

EXPLAIN ANALYZE SELECT id, user_id, order_amount
FROM orders
WHERE user_id = 100;

四、慢查询优化的几个实用方向

索引是慢查询优化的首选手段,但加索引不是越多越好。应根据 WHERE、ORDER BY、GROUP BY 中出现的列设计联合索引,并遵循最左前缀原则。例如查询条件为 user_id 且按 create_time 排序,可以创建联合索引 idx_user_time(user_id, create_time)。如果 SELECT 只查询索引包含的列,还能形成覆盖索引,避免回表。

ALTER TABLE orders ADD INDEX idx_user_time (user_id, create_time);

SELECT user_id, create_time FROM orders
WHERE user_id = 100
ORDER BY create_time DESC;

深分页是另一个常见问题。LIMIT 100000,20 会让 MySQL 先扫描大量行再丢弃前面的结果,数据量大了以后性能会很差。可以通过延迟关联的方式,先在子查询中只取主键,再回表获取完整记录,减少回表数据量。使用游标记录上一页最后一条 ID 也是更高效的做法。

SELECT o.id, o.user_id, o.order_amount
FROM orders o
INNER JOIN (
    SELECT id FROM orders
    WHERE user_id = 100
    ORDER BY id DESC
    LIMIT 100000, 20
) t ON o.id = t.id;

如果业务允许,还可以用游标方式替代大幅跳过:

SELECT id, user_id, order_amount
FROM orders
WHERE user_id = 100 AND id < 999999
ORDER BY id DESC
LIMIT 20;

还要避免在索引列上使用函数或做隐式类型转换。例如 WHERE DATE(create_time) = '2025-01-01' 会导致索引失效,因为函数作用在列上后优化器无法直接利用索引值。应改写为范围查询:

-- 不推荐:函数包裹索引列
SELECT * FROM orders WHERE DATE(create_time) = '2025-01-01';

-- 推荐:使用范围条件
SELECT * FROM orders
WHERE create_time >= '2025-01-01 00:00:00'
  AND create_time < '2025-01-02 00:00:00';

多表连接时,要确保连接列上有索引。优化器通常会选择小表驱动大表,但前提是有正确的统计信息。可以通过 ANALYZE TABLE 定期更新统计信息,避免优化器因统计信息过期做出错误选择。如果确实需要强制连接顺序,可以使用 STRAIGHT_JOIN,但不建议轻易使用,优先通过索引和统计信息让优化器自动判断。

五、形成可落地的慢查询排查闭环

排查慢查询不是一次性动作。线上环境应保持慢日志开启,并结合监控系统对慢查询数量、最大耗时设置报警。每次发版后可以统一分析慢日志,优先处理执行次数高且平均耗时长的 SQL。优化完成后需要对比 EXPLAIN 执行计划变化和实际响应时间,避免只降低扫描行数却增加回表次数。MySQL 8.0 的 sys 库提供了一些现成的语句摘要视图,适合日常巡检时快速查看全局慢语句。

SELECT query, exec_count, avg_latency, rows_sent_avg, rows_examined_avg
FROM sys.statement_analysis
WHERE db = 'orders'
ORDER BY avg_latency DESC
LIMIT 10;

sys.statement_analysis 的数据来自 performance_schema,它聚合了语句的执行次数、平均延迟、平均扫描行数等信息。相比直接翻慢日志,这个视图更适合用来做趋势观察和问题初筛。把慢日志记录、工具聚合、执行计划分析和索引优化串起来,就能形成从发现问题到验证效果的完整闭环,避免 SQL 性能问题反复出现。

MySQL慢查询慢查询日志SQL优化修改时间:2026-09-28 17:56:44

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0928/63078.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。