MySQL `EXPLAIN`:看懂执行计划、判断索引是否生效与排错套路

张开发
2026/4/16 22:58:38 15 分钟阅读

分享文章

MySQL `EXPLAIN`:看懂执行计划、判断索引是否生效与排错套路
目标你不仅能背出type/key/rows/Extra的含义还能在「明明有索引却很慢」时用执行计划把问题定位到索引没用上 / 用错索引 / 回表过多 / 排序与临时表。1. 为什么要看执行计划优化器的选择不等于你的直觉SQL 慢通常不是 MySQL “算不动”而是走错了访问路径扫太多行/页排序/分组触发 filesort 或临时表回表过多导致大量随机 IO执行计划就是你拿到的“证据”。2.EXPLAIN最重要的 6 个字段先记这 6 个就够用2.1type访问类型从好到坏的直觉顺序常见从优到劣不追求全背理解含义const/system按主键/唯一键等值命中一行ref非唯一索引等值匹配range范围扫描,between,like xxx%index全索引扫描扫索引树ALL全表扫描经验ALL大概率有问题除非表很小/统计类查询index也不一定好它可能在“扫完整个索引”2.2keypossible_keyspossible_keys优化器认为可能用的索引key最终选择的索引排错关键possible_keys有但key为空索引可能被写法破坏函数、类型转换、OR 等key选了但仍慢可能回表大、选择性差、排序分组没被覆盖2.3rows预计扫描行数不是返回行数rows越大越可能慢观察rows与实际返回行数的倍率例如返回 20 行却扫描 20 万行说明过滤不够“靠前”2.4filtered过滤比例估计值低意味着即使走索引也过滤不掉多少常见于索引选择性差、条件不够精确2.5Extra优化器额外动作面试/排错高频重点识别Using index覆盖索引不回表Using where还需要 server 层过滤Using temporary用了临时表group by / order by 可能触发Using filesort需要额外排序不一定是文件表示额外排序过程经验Using temporaryUsing filesort是性能红灯2.6 一个可复现的最小例子用同一张表看清type/key/rows/Extra先准备一张“很像业务表”的表既有等值查询、也有范围、也有分页与排序。createtablet_order(idbigintprimarykey,user_idbigintnotnull,statustinyintnotnull,create_timedatetimenotnull,amountintnotnull,titlevarchar(64)notnull,keyidx_user_time(user_id,create_time,id),keyidx_status_time(status,create_time));接下来用同一张表的不同查询观察执行计划的关键差异。例 1等值命中最理想const/refexplainselect*fromt_orderwhereid123;你期望看到type接近constkeyPRIMARYrows很小≈1例 2范围扫描rangeexplainselectid,user_id,create_timefromt_orderwhereuser_id1andcreate_time2026-04-01 00:00:00andcreate_time2026-04-02 00:00:00orderbycreate_timedesc,iddesclimit20;你期望看到key命中idx_user_timetype至少range或ref 范围下探视数据分布与优化器而定Extra尽量不要出现Using filesort如果这里出现Using filesort通常说明排序字段与索引顺序不一致或优化器无法利用索引完成排序例 3回表放大key选对了但仍然慢explainselect*fromt_orderwhereuser_id1orderbycreate_timedesclimit10000,20;即使key命中idx_user_time它仍可能很慢因为offset 本质是“扫描并丢弃”select *会导致大量回表此时你应该结合rows是否随页码变大Extra是否缺少Using index无法覆盖来判断瓶颈是否是“回表与大分页”。3. 判断“索引是否生效”的快速规则key是否不为空type是否至少到range/ref/constrows是否明显下降Extra是否出现Using filesort/temporary索引“生效”不是看key有值就结束而是看它有没有把rows降到可接受范围并避免额外排序/临时表。4. 常见“索引没用上”的 7 种原因面试高频4.1 对索引列做函数/表达式where date(create_time) 2026-04-03破坏有序性无法范围定位修复改成范围条件create_time 2026-04-03 00:00:00 and create_time 2026-04-04 00:00:00对照组建议面试这样讲错误写法破坏索引有序性wheredate(create_time)2026-04-03正确写法可范围定位wherecreate_time2026-04-03 00:00:00andcreate_time2026-04-04 00:00:004.2 隐式类型转换varchar_col 123可能触发转换导致索引失效对照组错where phone 13800138000phone 是 varchar对where phone 138001380004.3OR条件可能导致优化器放弃索引或改成 union 才能用对照组错where a 1 or b 2容易走全表或选择性差的路径对拆成union all前提两段条件互斥或你能接受去重逻辑4.4like %xxx前缀%无法利用 B 树前缀有序对照组错like %abc对like abc%可以范围扫描4.5 联合索引未满足最左前缀索引(a,b,c)只用b条件通常不走对照组错where b 2缺少 a对where a 1 and b 24.6 返回列过多导致回表巨大select * 二级索引过滤最终回表 N 次典型修复方向列表页只取必要列尽量覆盖索引必须返回全字段用“延迟关联”先查主键 limit再回表取整行4.7 统计信息不准/数据倾斜优化器误判成本选择了全表或错误索引处理analyze table谨慎低峰重建索引/优化 SQL补充说明统计信息不准最容易出现的现象是同一条 SQL不同参数走不同计划参数敏感你在测试环境很快线上慢因为线上数据分布不同5. 排序/分组为什么慢filesort与temporary的根因常见触发order by字段与 where 使用的索引不一致order by多列顺序与联合索引不一致group by需要额外聚合优化方向让where order by尽可能使用同一个联合索引让查询走覆盖索引减少回表5.1 一个可解释的filesort例子索引能过滤但无法按要求有序输出-- 假设有索引 (user_id, create_time)select*fromt_orderwhereuser_id1orderbyamountdesclimit20;这类查询可能where 能用索引定位 user_id但 order by 不是索引顺序仍需要额外排序Using filesort优化手段调整索引使其覆盖排序例如把 amount 纳入联合索引且满足 where order或接受排序成本但缩小参与排序的数据量更强过滤5.2 一个高频temporary场景group by 与 order by 不一致selectstatus,count(*)fromt_orderwhereuser_id1groupbystatusorderbycreate_timedesc;如果聚合维度与排序维度无法用同一个索引顺序完成可能出现临时表。通常建议先把查询目标说清到底要按 status 聚合还是要按时间排序对报表类查询考虑离线聚合或预聚合表6. 线上排查手册从现象到动作现象偶发慢看是否锁等待SHOW ENGINE INNODB STATUS/ performance_schema现象一直慢先EXPLAIN确认rows与Extra抓慢日志 SQL 参数确认是否存在“参数导致走不同计划”参数敏感现象扫描行巨大但索引存在查索引选择性、是否最左前缀、是否被函数/转换破坏6.1 一套“先证据、再动作”的 checklist更像线上排障先确认是不是“DB 慢”还是“等锁慢”RT 高但 CPU 不高、连接堆积优先怀疑锁等待固定证据同一条 SQL 同一组参数慢日志/链路追踪拿到 SQL 与参数EXPLAIN只回答 4 个问题用了哪个索引key扫了多少rows是哪种访问type有没有额外代价Extrafilesort/temporary/Using index结合现象选择动作ALL/rows巨大先修 where 与索引Using filesort/temporary修 whereorder/group 的联合索引与写法key正确但仍慢优先排查回表、offset、大结果集偶发慢排查锁等待、热点更新、长事务7. 面试背诵稿60 秒我会用EXPLAIN看执行计划来判断 SQL 慢的根因。重点看type访问类型、key/possible_keys是否用到正确索引、rows预计扫描行数和Extra是否出现Using temporary、Using filesort、是否覆盖索引Using index。索引不生效常见原因是对索引列做函数、隐式类型转换、like %xxx、OR、联合索引没走最左前缀以及回表过多导致随机 IO。优化时我会尽量让where order by走同一个联合索引并用覆盖索引减少回表同时注意统计信息不准导致的计划误选。

更多文章