MySQL 慢查询日志分析与索引优化实战指南

MySQL 慢查询日志分析与索引优化全景

本文教你如何在两小时内把 MySQL 8.0 上一组秒级慢查询,通过慢查询日志、EXPLAIN(MySQL 的执行计划查看工具,用来展示一条查询如何选择索引与连接方式)、复合索引三件套压到毫秒级,给出一份可在生产环境直接复用的诊断与优化流程。读完本文你会得到三件可直接落地的产出:第一,慢查询日志的开启参数表与按行解读模板,避免日志写爆磁盘;第二,pt-query-digest 报告字段的实战解读思路,定位 top 5 高耗时模式;第三,一组覆盖范围查询、排序、分组、覆盖索引的实战调优案例,让团队建立可复制的索引设计直觉,告别”拍脑袋加索引”。

一、为什么先看慢查询日志再动手优化

如何在不打扰业务高峰、不全表扫描的前提下,准确找到拖慢系统的那十几条 SQL,是性能调优的第一道关。盲目加索引会带来三个反作用:写入放大、空间膨胀、优化器误判。解决问题的正确顺序应该是先采集证据再下结论,慢查询日志就是最直接的证据来源。MySQL 慢查询日志会记录所有执行时间超过 long_query_time 阈值的语句,配合 log_queries_not_using_indexes 参数还能把没走索引的全表扫描一并捞出。

把这件事做扎实需要两个前提:一是数据库实例所在的服务器磁盘 I/O 能跟上日志写入,二是有足够的盘容量让日志至少保留 24 小时。如果你正在为新业务挑选数据库主机,可以先评估底层资源,参考云服务器选购指南里的 IOPS 与磁盘读写部分。云服务器(即通过虚拟化技术从物理服务器集群中切分出来的弹性计算资源)在做 OLTP 场景时对随机 I/O 的依赖很高,SATA SSD 与 NVMe SSD 之间的差距会在压测阶段被放大数十倍,影响整体优化空间。

二、开启慢查询日志并采集第一份报告

在 my.cnf 的 mysqld 段加入下面这些参数后,重启或 reload 即可开始采集。核心配置要点:

  • slow_query_log = 1:总开关
  • slow_query_log_file = /var/log/mysql/slow.log:日志路径
  • long_query_time = 1:阈值 1 秒,写多业务建议先用 1 秒再调到 0.5 秒
  • log_queries_not_using_indexes = 1:记录未走索引的查询
  • log_slow_admin_statements = 1:把 ALTER 与 ANALYZE 也纳入
  • log_slow_extra = 1:MySQL 8.0 新增的额外耗时维度

采集 4 到 24 小时后,用 mysqldumpslow -s t -t 20 slow.log 拿到按耗时排序的前 20 条;进一步用 pt-query-digest slow.log 生成详细报告,看 Response time、Calls、R/Call、Rows examined 四个核心列。如果数据库部署在 VPS(Virtual Private Server,虚拟专用服务器,通过虚拟化划分的独立实例)上,建议把日志切片归档到对象存储,避免本地盘占满。需要远程拉取日志的流程可以参考美国 VPS 部署教程里的 SSH 与文件同步部分。主流云厂商的 VPS 实例(在磁盘性能与带宽,也就是网络出口的数据传输速率,单位 Mbps 上)都能稳定承载这类日志采集压力。

三、用 EXPLAIN 拆解执行计划与索引设计

拿到 top N 慢 SQL 后,下一步用 EXPLAIN 看执行计划。重点关注六个字段:

  • type:const、ref、range、index、ALL 由优到劣,看到 ALL 几乎一定有问题
  • key:实际使用的索引,NULL 表示没走任何索引
  • rows:估算扫描的行数,越接近最终返回行数越好
  • Extra:Using index 是覆盖索引最佳,Using filesort 与 Using temporary 都要警惕
  • key_len:复合索引实际使用了几个字段
  • ref:连接条件使用的列或常量

针对 WHERE a = ? AND b > ? ORDER BY c 这类组合,复合索引的字段顺序应该按等值在前、范围在中、排序在后的原则设计。如果排序字段和范围字段冲突,往往要在两个执行计划里做取舍。覆盖索引(即查询所需字段全部包含在索引内,无需回表)是优化高频查询的杀手锏,能把回表(每命中一行索引就要去主键表取数据的过程)从二次 I/O 压缩到零次。

四、四类典型场景的优化套路与下一步建议

按业务出现频率,把慢查询归到四类常见场景:

  • 范围查询拖慢:在范围字段后面追加排序字段做联合索引
  • 分页深翻拖慢:用主键 cursor 替代 LIMIT offset
  • IN 大集合拖慢:把 IN 列表换成临时表 join,或对子查询字段加索引
  • 模糊匹配拖慢:LIKE 前缀确定的可加 BTREE 索引,全模糊改走全文索引或外部检索

每一类场景在 EXPLAIN 上都有典型特征,团队可以把这份对照表沉淀成内部 wiki,新人遇到慢 SQL 直接对号入座。完成优化后,建议把 long_query_time 调到 0.3 秒做二次扫描,再用 pt-query-digest 做前后对比,确认 top SQL 的耗时分布真的下降。如果你的 WordPress 站点也面临数据库瓶颈,建议结合WordPress 香港主机购买指南里的主机选型与WordPress 性能调优实战的对象缓存策略一起做,缓存层能拦截 60% 以上的重复查询。总结来说,慢查询日志是排查 MySQL 性能问题最有效的入口,建议你今天就开启日志、采集 4 小时、跑一次 pt-query-digest,把团队的优化节奏从被动救火切换到主动巡检,再把可复用的索引设计原则沉淀进代码评审清单。后续可以把这套流程接入 Prometheus 与 Grafana,做到 long_query_time 突破阈值时自动告警,让性能回归在分钟级被发现。团队层面可以把索引设计、慢查询治理、定期 EXPLAIN 复核三件事写进数据库变更评审清单,每个迭代固定花半小时回顾上周的 top 10 慢查询走势。当业务体量进一步增长,可以引入读写分离、分库分表、ProxySQL 路由等架构演进手段,把单机优化的天花板向上拓展。索引并不是越多越好,每个写入操作都要更新所有相关索引,过度堆叠会让 INSERT 与 UPDATE 的耗时显著上升,得到慢查询日志改善的同时反而引入写入性能退化。

建议你今天就着手做三件事:第一,按本文参数表打开 slow.log;第二,跑一次 pt-query-digest 报告并保存基线;第三,把 top 3 慢查询按 EXPLAIN 拆解一遍,可以考虑先用覆盖索引收敛回表。

发表评论