MySQL 慢查询治理:从 slow log 到执行计划的三层排查

举报
yd_251844643 发表于 2026/10/21 13:14:04 2026/10/21
【摘要】 把慢查询治理拆成三层:先用 slow log 做聚合定位,再用执行计划判断瓶颈类型,最后才是索引与统计信息调整。附一份可直接套用的排查顺序和几个常见误区。

慢查询治理最常见的误区,是一上来就加索引。加完之后发现没变快,或者快了一阵又慢回去,于是继续加。真正有效的做法是先定位瓶颈类型,再决定动作。下面这套三层排查顺序,基本可以覆盖日常 80% 的场景。


一、第一层:用 slow log 做聚合,不做逐条分析


慢查询日志的价值不在单条 SQL,而在聚合后的分布。开启方式:


SET GLOBAL slow_query_log = ON;

SET GLOBAL long_query_time = 1;

SET GLOBAL log_queries_not_using_indexes = ON;


注意 long_query_time 建议先用 1 秒起步,不要一上来设 0.1,否则日志量会把磁盘和 IO 打满。log_queries_not_using_indexes 在小表多的实例上也会产生大量噪音,可以配合 min_examined_row_limit 限制。


拿到日志后不要肉眼翻,用 pt-query-digest 聚合:


pt-query-digest /var/log/mysql/slow.log > report.txt


重点看三个指标:


1)总耗时占比。排在前面的几条往往贡献了 60% 以上的总耗时,先解决它们。

2)执行次数。单次 3 秒但一天跑 1 次,和单次 30 毫秒但一天跑 10 万次,后者对系统的影响更大。

3)Rows_examined 与 Rows_sent 的比值。比值越大,说明扫了很多行才返回少量结果,通常是索引缺失或选择性差。


二、第二层:看执行计划,判断瓶颈类型


拿到可疑 SQL 后先 EXPLAIN,重点看这几列:


1)type。从好到坏大致是 system > const > eq_ref > ref > range > index > ALL。出现 ALL 代表全表扫描,通常是索引缺失;出现 index 代表扫了整个索引树,也不理想。

2)key。实际用到的索引。如果 possible_keys 有值但 key 为 NULL,说明优化器放弃了索引,常见原因是选择性太低或数据分布倾斜。

3)rows。预估扫描行数。和实际差很多时,说明统计信息过期。

4)Extra。出现 Using filesort 表示需要额外排序,Using temporary 表示用了临时表,这两个在数据量大时是主要开销来源。


MySQL 8.0.18 之后可以用 EXPLAIN ANALYZE,直接给出实际执行耗时和真实行数,比估算值可靠得多:


EXPLAIN ANALYZE SELECT ...;


如果发现 rows 估算与实际差一个数量级,先别急着改 SQL,跑一次 ANALYZE TABLE 更新统计信息,很多「突然变慢」的案例到这一步就解决了。


三、第三层:索引与写法,只在确认需要时动


到这一步再谈加索引,判断依据是两个:选择性和覆盖度。


1)选择性。选择性 = 不重复值 / 总行数。接近 1 的列适合做索引,接近 0 的列(比如性别、状态位)单独建索引收益很低。

2)联合索引的顺序。遵循最左前缀,把等值条件里选择性高的放前面,范围条件放最后。例如 WHERE tenant_id = ? AND status = ? AND created_at > ?,索引顺序建议 (tenant_id, status, created_at)。

3)覆盖索引。如果查询只取少数几列,把这几列带上可以避免回表。EXPLAIN 里 Extra 出现 Using index 就说明命中了覆盖索引。


几个容易被忽略的写法问题:


1)隐式类型转换。字符串字段用数字去比(WHERE phone = 13800138000)会让索引失效,必须写成字符串。

2)函数包裹列。WHERE DATE(created_at) = 2026-10-21 无法走索引,改成范围查询 WHERE created_at >= 2026-10-21 AND created_at < 2026-10-22。

3)前导通配符。LIKE %abc 无法走索引,只能靠全文索引或改写检索方式。


四、几个常见的判断误区


1)索引不是越多越好。每个索引都会带来写入放大,也会增加优化器的选择成本。一个高频更新表上堆十几个索引,写性能下降往往比查询收益更明显。

2)不要只看平均耗时。平均 50 毫秒但 P99 到 3 秒,说明存在偶发的执行计划劣化或锁等待,平均值会把它掩盖掉。

3)避免在从库上做分析。从库的数据分布、索引状态可能和主库不一致,得出的结论未必适用。

4)优化后要回归。改索引后观察一段时间,确认没有把别的 SQL 拖慢——优化器可能因此换了执行计划。


五、可以照抄的落地顺序


1. 打开 slow log,用 pt-query-digest 聚合出 Top 10

2. 按总耗时和执行次数排序,选出优先处理的三条

3. 逐条 EXPLAIN / EXPLAIN ANALYZE,判断是全表扫描、排序、临时表还是回表

4. 先试统计信息更新,再试索引调整,最后才改 SQL 结构

5. 每改一项,对比优化前后的 Rows_examined 与实际耗时

6. 把结论记录到文档,避免同样的问题反复排查


慢查询治理的难点从来不是「不知道加索引」,而是判断该不该加、加在哪、以及加完之后有没有副作用。把顺序固定下来,比记住更多技巧更有用。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。