第一次在慢查询日志里发现那条执行时间超过8秒的SQL时,我盯着explain输出的十几行结果手足无措。type列里的ALL意味着全表扫描,rows显示要检查200万行数据——这就是性能瓶颈的罪魁祸首。从那天起,我养成了在每条重要SQL前加上EXPLAIN的习惯。
执行计划是MySQL优化器的作战地图,通过EXPLAIN命令可以获取查询的详细执行策略。这张地图用12个关键字段揭示查询如何访问数据、使用哪些索引、预估处理多少行数据等核心信息。理解这些字段的含义,就像拿到了数据库性能调优的X光片。
每个SELECT语句都会被分配唯一的id标识符。当出现复杂查询时,id相同的数字表示这些操作属于同一个执行单元。我曾在优化一个包含5层子查询的报表SQL时,通过id值快速定位到最耗时的子查询模块。
select_type字段则揭示了查询的类型:
实战经验:DERIVED类型往往伴随性能问题,我曾将某个DERIVED查询改写为JOIN后,执行时间从12秒降至0.3秒
这个字段直接反映了查询的访问效率,按性能从优到劣排序:
上周排查的一个案例:某接口频繁超时,发现其SQL的type是ALL且扫描70万行数据。添加适当索引后变为ref类型,扫描行数降至3行,响应时间从2秒变成20毫秒。
possible_keys显示可能用到的索引,而key是优化器实际选择的索引。当这两个字段值不一致时,往往意味着:
rows是预估检查的行数,filtered表示存储引擎层过滤后剩余数据的百分比。这两个值相乘可以估算出最终结果集大小。在优化一个分页查询时,我发现虽然rows只有1000,但filtered低至1%,导致实际要处理10万行数据——这就是分页慢的根本原因。
这个字段包含数十种补充信息,其中几个关键值需要特别关注:
最近优化过一个分组查询,Extra显示"Using temporary; Using filesort",创建包含分组字段和排序字段的联合索引后,这两个额外操作都消失了,查询速度提升15倍。
当表使用分区时,这里显示命中的分区编号。有次排查慢查询,发现该字段显示查询扫描了全部分区,通过修改WHERE条件使其命中特定分区后,查询时间从分钟级降到秒级。
某商品表有联合索引(category_id, status),但查询WHERE status=1 AND category_id IN(10,20)时却走了全表扫描。这是因为IN条件破坏了索引最左前缀原则,改为分别查询category_id=10和20再UNION后,查询效率提升40倍。
通过调整多表连接的顺序,可以显著改变执行计划。有次优化6表关联查询,发现MySQL选择的连接顺序导致中间结果集膨胀到百万行。使用STRAIGHT_JOIN强制连接顺序后,执行时间从8秒降至0.5秒。
传统表格形式有时难以理解复杂查询,使用JSON格式输出可以获取更详细的信息:
sql复制EXPLAIN FORMAT=JSON
SELECT * FROM orders WHERE user_id=100;
输出包含成本估算、访问方法细节等扩展信息,适合深度分析。
这个增强版命令会实际执行查询并返回真实耗时:
sql复制EXPLAIN ANALYZE
SELECT * FROM products WHERE price>100;
我在排查一个索引失效问题时,通过对比预估rows和实际rows,发现统计信息严重过期,执行ANALYZE TABLE后查询性能立即恢复。
过度依赖可视化工具:Navicat等工具的解释功能可能隐藏细节,建议始终查看原始EXPLAIN输出
忽视基数估算错误:当预估rows和实际rows差异超过10倍时,应该更新统计信息
盲目添加索引:每个新索引都会增加写操作成本,我曾经见过一个表有20个索引导致写入性能下降90%
忽略查询重写:有时修改SQL写法比加索引更有效。比如把WHERE DATE(create_time)='2023-01-01'改为范围查询WHERE create_time BETWEEN '2023-01-01 00:00:00' AND '2023-01-01 23:59:59'就能利用索引
执行计划分析就像数据库医生的听诊器,需要结合具体业务场景反复实践。每次调优后记录前后的EXPLAIN结果对比,长期积累就会形成准确的性能直觉。我现在的习惯是:任何执行超过0.5秒的SQL都必须经过EXPLAIN检验,这个简单的习惯让系统整体性能提升了60%以上。