为什么查询语句优化要先定位慢查询的执行计划

为什么查询语句优化要先定位慢查询的执行计划

1024KB1024KB 技术SEO 2026-10-02 01:31:55 浏览 77262 字数 1616

查询语句优化的核心起点是先拿到慢查询的执行计划,没有执行计划的一切优化都是猜测,执行计划会直接告诉你索引有没有被使用、扫描了多少行、临时表和排序发生在哪一步,据MySQL官方文档《MySQL 8.0 Reference Manual—EXPLAIN Output Format》的说明,EXPLAIN输出的每行记录都对应优化器为查询语句选择的具体执行路径,这个路径就是优化动作的唯一依据。

执行计划中哪些指标最需要关注

执行计划里最有价值的三个字段是type、rows和Extra,type反映访问类型,rows反映预估扫描行数,Extra里藏着排序、临时表和文件索引用不到的信号。

type值含义优化优先级
system/const表中只有一行或匹配主键的唯一行最优
eq_ref主键或唯一索引被等值匹配优
ref非唯一索引等值匹配良好
range索引范围扫描可接受
index全索引扫描需警惕
ALL全表扫描必须避免

当type字段出现ALL或者rows的估算值比实际表行数还要大时,查询语句优化就进入了明确的方向,要么修改SQL写法让索引生效,要么检查表统计信息是否需要更新。

用EXPLAIN定位慢查询的实际步骤

拿到一条慢SQL之后先执行EXPLAIN再看实际耗时,这个顺序不能颠倒,在MySQL中执行EXPLAIN SELECT语句即可输出执行计划,在慢查询日志中把耗时超过阈值的SQL捞出来逐条分析。

EXPLAIN SELECT u.username, o.order_no
FROM user u
INNER JOIN orders o ON u.id = o.user_id
WHERE u.status = 1
ORDER BY o.created_at DESC
LIMIT 20;

如果看到orders表的type是ALL,说明这条查询语句优化需要从JOIN条件入手,检查o.user_id字段上是否有索引,同时ORDER BY的字段要和索引顺序匹配才能避免filesort。

索引没有被使用的几种典型场景

查询语句优化中最高频的问题是索引明明存在却没有生效,函数包裹、隐式类型转换和LIKE前置通配符是让索引失效的三个常见原因,对索引列使用函数会让优化器放弃索引扫描。

  • WHERE DATE(create_time) = '2025-01-01' 应改写为 create_time BETWEEN '2025-01-01 00:00:00' AND '2025-01-01 23:59:59'
  • WHERE phone = 13812345678 如果phone是varchar类型则发生隐式转换,导致索引失效
  • WHERE name LIKE '%关键词%' 因为无法确定前缀所以索引不可用

这些场景在《高性能MySQL(第4版)》中有清晰的数据支持,作者Silvia Botros和Jeremy Tinley指出索引列上的函数和运算都会使优化器无法使用B+树的顺序查找能力,这个结论是数据库性能优化领域公认的基础共识。

一个真实业务场景的查询语句优化实例

某电子商务平台的订单查询接口响应时间从800毫秒飙升到6秒,通过EXPLAIN发现订单表状态字段的索引被跳过了,原始SQL用status=2 AND created_at > NOW() - INTERVAL 7 DAY,但created_at和status分别有独立索引,MySQL优化器只选了一个。

联合索引(order_status, created_at)是这里的最佳解法,重建索引后同样查询语句的时间降到120毫秒,查询语句优化的效果不是线性提升而是数量级的变化,这个案例印证了执行计划对优化方向的直接引导价值。

排序优化与LIMIT深分页的代价

慢查询里很大比例来自深分页,LIMIT 1000000, 20会让数据库扫描前一百万行再丢弃,查询语句优化需要改变读取策略,把随机跳过的分页改成基于游标的键集分页。

-- 传统深分页(需要扫描100万行)
SELECT id, title FROM articles ORDER BY id LIMIT 1000000, 20;

-- 键集分页(基于上次查询的最大id)
SELECT id, title FROM articles 
WHERE id > 1000000 
ORDER BY id 
LIMIT 20;

键集分页的前提是排序字段必须有唯一索引约束,如果业务需要按created_at排序但该字段存在重复值,可以改为主键id排序然后按需回查created_at,查询语句优化的原则是让每次扫描的数据量和实际返回的数据量接近。

Extra字段中的filesort意味着什么

EXPLAIN输出中Extra出现Using filesort表明MySQL需要额外的排序操作,执行计划里的排序会消耗内存和CPU,数据量超过sort_buffer_size时会落到磁盘临时文件,这个成本比索引排序高出几个数量级。

  • ORDER BY的字段需要在同一个索引中且顺序一致
  • 升序降序混用ORDER BY col1 ASC, col2 DESC时索引也可能失效
  • GROUP BY和DISTINCT同样需要索引支持

统计信息过旧导致执行计划偏差

查询语句优化还有一个容易被忽略的因素是表统计信息没有及时更新,MySQL优化器依靠统计信息估算行数,当表的实际数据量发生剧烈变化而统计信息滞后时,优化器会选择错误的执行计划。

ANALYZE TABLE命令可以重新收集表的统计信息,在写入量大的表上建议在业务低峰期定期执行这个操作,据Percona的数据库专家Baron Schwartz在《高性能MySQL》中的建议,频繁更新的表按照索引列的基数变化频率定期刷新统计信息,可以显著减少因估算偏差导致的慢查询。

查询语句优化从执行计划开始也以执行计划结束

一条SQL的优化是否真正完成不是看响应时间变得多短,而是看执行计划中还有没有warning级别的提示,以及Extra字段是否还存在临时表和文件排序。

把慢查询日志、EXPLAIN和performance_schema的事件统计结合起来,就能形成一个完整的查询语句优化闭环,每次优化动作之后同样用EXPLAIN验证执行计划的变化,用真实耗时数据对比优化前后的差异,这样沉淀下来的经验才是可复用的。

分享到:

评论

加载中...
我
还没有评论,快来抢沙发吧~