【Mysql】执行计划的分析

发布时间:2026/5/31 0:34:23

【Mysql】执行计划的分析 执行计划你可以理解成MySQL 在真正执行 SQL 前先给这条 SQL 制定的一份“执行路线图”。比如一条 SQLselect*fromuserwhereage18orderbycreate_time;MySQL 不会直接傻傻执行而是会先想“我要不要用索引”“用哪个索引”“先查哪张表”“预计要扫描多少行”“排序能不能用索引完成”“需不需要临时表”这些信息就会体现在EXPLAIN的结果里。一、为什么要看执行计划优化 SQL 的第一步就是先知道这条 SQL到底是怎么执行的。比如你以为 SQL 走了索引select*fromuserwherename张三;但通过执行计划发现type ALL key NULL rows 1000000这就说明它根本没走索引而是在全表扫描 100 万行。所以执行计划的作用就是帮你判断有没有走索引走的是哪个索引扫描的数据量大不大有没有全表扫描有没有文件排序有没有临时表多表查询时表的执行顺序是什么。二、执行计划怎么看一般这样使用EXPLAINselect*fromuserwhereage18;MySQL 会返回一张表这张表通常有 12 列。重点不需要每一列都背得特别死面试和实际优化时重点看这几个id select_type table type possible_keys key key_len rows Extra其中最重要的是type、key、rows、Extra三、核心字段解释1. idSQL 的执行顺序id表示 SELECT 的执行顺序。规则id 相同从上往下执行 id 不同id 越大越先执行比如select*fromuserwheredept_id(selectidfromdeptwherename技术部);这里有外层查询和子查询。执行计划里可能会出现两个idid 1 外层 user 查询 id 2 子查询 dept 查询因为id 2更大所以子查询先执行。你可以理解为MySQL 先查出技术部的 id再拿这个 id 去 user 表里查员工。2. select_type查询类型select_type表示当前这一行对应的 SELECT 是什么类型。常见的有SIMPLE简单查询不包含子查询和 UNION。select*fromuserwhereid1;执行计划中select_type SIMPLEPRIMARY如果 SQL 中包含子查询那么最外层的 SELECT 就是PRIMARY。select*fromuserwheredept_id(selectidfromdeptwherename技术部);外层这个select*fromuser...就是select_type PRIMARYSUBQUERY子查询中的 SELECT。selectidfromdeptwherename技术部这一部分就是select_type SUBQUERYDERIVEDFROM 后面的子查询也叫派生表。select*from(select*fromuserwhereage18)t;这个子查询结果会被当成一张临时表t所以它是select_type DERIVED3. table正在访问哪张表这个字段表示当前这一行执行计划访问的是哪张表。例如select*fromuserujoindept donu.dept_idd.id;执行计划中可能有两行table u table d表示 MySQL 需要分别访问user表和dept表。4. type访问类型最重要字段之一type表示 MySQL 是用什么方式访问表的。它非常重要因为它直接反映 SQL 的性能。从好到差大致是system const eq_ref ref range index ALL实际看执行计划时重点记住这些就够了const 很好 eq_ref 很好 ref 较好 range 可以接受 index 一般 ALL 较差四、type 常见值解释1. systemsystem是最特殊的一种表中只有一行数据直接返回。这个很少见了解即可。2. constconst表示通过主键索引或唯一索引一次就能定位到一条数据。例如select*fromuserwhereid1;如果id是主键那么执行计划可能是type const key PRIMARY因为主键唯一id 1最多只能查出一条记录。所以 MySQL 认为这张表可以当成一个常量来处理。3. eq_refeq_ref常见于多表 JOIN。它表示前一张表查出一行数据后拿这一行的数据去当前表中匹配当前表最多只匹配一行。例如select*fromorder_info ojoinuseruono.user_idu.id;如果u.id是主键那么对于订单表中的每一条订单记录都只能匹配到一个用户。所以访问user表时可能是type eq_ref你可以理解为每一条订单只对应一个用户。这是非常好的 JOIN 方式。4. refref表示使用了普通索引但可能匹配多行。例如select*fromuserwhereage18;如果age上有普通索引那么执行计划可能是type ref key idx_age因为年龄为 18 的用户可能有很多个所以不是唯一匹配。它比全表扫描好很多但不如主键或唯一索引查询。5. rangerange表示范围查询。例如select*fromuserwhereage18;或者select*fromuserwhereagebetween18and30;如果age有索引执行计划可能是type range key idx_age它表示 MySQL 会在索引树上找一个范围而不是扫描全表。6. indexindex表示扫描整棵索引树。例如selectidfromuser;如果id是索引列MySQL 可能只扫描索引不扫描整张表。执行计划可能是type index它和ALL有点像都是“全量扫描”。区别是index扫描整棵索引树 ALL扫描整张表一般来说索引比表小所以index通常比ALL快。但是它依然不是特别理想因为还是扫描了很多数据。7. ALLALL表示全表扫描。例如select*fromuserwherename张三;如果name没有索引就可能是type ALL key NULL这表示 MySQL 要从第一行扫到最后一行。如果表数据很大性能就会很差。优化 SQL 时一般要重点关注type ALL五、possible_keys 和 key这两个字段经常一起看。possible_keys可能用到的索引表示 MySQL 觉得这条 SQL可能可以使用哪些索引。比如possible_keys idx_name, idx_age说明优化器认为这两个索引都有可能用。key实际使用的索引表示 MySQL 最终真正选择了哪个索引。例如possible_keys idx_name, idx_age key idx_age说明可能用idx_name或idx_age但最终选择了idx_age。如果key NULL说明没有使用索引。这是一个非常危险的信号尤其是大表查询时。六、key_len索引使用长度key_len表示 MySQL 实际使用了索引的多少字节。它可以用来判断联合索引到底用了几个字段。例如有联合索引indexidx_name_age(name,age)如果 SQL 是select*fromuserwherename张三;可能只用到了联合索引的name部分。如果 SQL 是select*fromuserwherename张三andage18;可能用到了name age两个部分。key_len越长通常说明使用的索引字段越多。不过面试里一般不用你手算字节数知道它能判断索引使用程度即可。七、rows预计扫描行数rows表示 MySQL 预计为了找到结果需要扫描多少行。例如rows 10说明预计扫描 10 行。rows 100000说明预计扫描 10 万行。这个值越小越好。不过注意它是 MySQL 估算的不一定百分百准确。优化 SQL 时通常希望rows 越小越好如果你看到type ALL rows 5000000那基本说明这条 SQL 很危险可能会扫 500 万行。八、filtered过滤后剩余比例filtered表示经过条件过滤后预计剩下多少百分比的数据。例如rows 10000 filtered 10意思是 MySQL 预计扫描 10000 行然后经过 WHERE 条件过滤后保留大约 10%。也就是最终大概剩下10000 * 10% 1000 行这个字段一般辅助看不是最核心。九、Extra额外信息非常重要Extra表示 MySQL 执行 SQL 时的一些额外操作。它非常关键因为很多性能问题都能从这里看出来。1. Using index这是比较好的情况。表示使用了覆盖索引不需要回表。例如有索引indexidx_name_age(name,age)执行selectname,agefromuserwherename张三;因为查询的字段name、age都在索引里面所以 MySQL 直接从索引中拿数据不需要再回到主键索引找完整记录。执行计划中可能出现Extra Using index这通常是好事。2. Using where表示 MySQL 需要使用 WHERE 条件进行过滤。例如select*fromuserwhereage18;执行计划可能出现Extra Using where它不一定是坏事。但是如果同时出现type ALL Extra Using where那就说明MySQL 正在全表扫描然后一行一行用 WHERE 条件过滤。这种情况性能可能较差。3. Using filesort这个要重点注意。Using filesort表示 MySQL 无法利用索引完成排序需要额外排序。例如select*fromuserwhereage18orderbycreate_time;如果没有合适的索引支持order by create_time就可能出现Extra Using filesort注意filesort不一定真的在磁盘文件中排序它表示 MySQL 使用了额外的排序算法。但是它通常意味着排序成本较高。优化方向通常是给排序字段建立合适索引比如indexidx_age_create_time(age,create_time)这样可以先按age定位再按create_time的索引顺序返回。4. Using temporary这个也要重点注意。Using temporary表示 MySQL 需要创建临时表保存中间结果。常见于groupbyorderbydistinctunion例如selectage,count(*)fromusergroupbyage;如果没有合适索引可能会出现Extra Using temporary这说明 MySQL 需要先把中间结果放到临时表里再继续处理。如果数据量大性能可能比较差。5. Using index condition表示使用了索引条件下推也叫 ICPIndex Condition Pushdown。简单理解原本一些过滤条件要回表后才能判断现在可以先在索引层面判断一部分减少回表次数。例如有联合索引indexidx_name_age(name,age)SQLselect*fromuserwherenamelike张%andage18;MySQL 可以在扫描索引时尽量先判断索引中已有的条件减少回表。执行计划中可能出现Extra Using index condition这通常是一个优化行为。6. Using join buffer这个常见于 JOIN 查询。例如select*fromuserujoinorder_info oonu.ido.user_id;如果被驱动表的连接字段没有索引MySQL 可能会使用 join buffer。执行计划中可能出现Using join buffer它的意思是MySQL 先把一部分驱动表数据放到 join buffer 里然后再和被驱动表做匹配。这通常说明 JOIN 的被驱动表没有很好地使用索引。优化方向一般是给被驱动表的关联字段加索引比如order_info.user_id应该建立索引。十、用一个例子完整理解假设有一张用户表createtableuser(idintprimarykey,namevarchar(50),ageint,cityvarchar(50),create_timedatetime,indexidx_age(age),indexidx_name_age(name,age));现在执行EXPLAINselect*fromuserwhereage18;可能结果id: 1 select_type: SIMPLE table: user type: ref possible_keys: idx_age key: idx_age rows: 100 Extra: Using where解释id 1 表示这是一个简单查询。 select_type SIMPLE 表示没有子查询也没有 UNION。 table user 表示查询 user 表。 type ref 表示使用普通索引进行等值查询。 possible_keys idx_age 表示可能使用 idx_age 索引。 key idx_age 表示实际使用了 idx_age 索引。 rows 100 表示预计扫描 100 行。 Extra Using where 表示还需要根据 where 条件进行过滤。这条 SQL 基本还可以。再看一个不好的例子EXPLAINselect*fromuserwherecity北京;如果city没有索引可能是type: ALL possible_keys: NULL key: NULL rows: 1000000 Extra: Using where解释type ALL 表示全表扫描。 key NULL 表示没有使用索引。 rows 1000000 表示预计扫描 100 万行。 Extra Using where 表示扫描之后再用 where 过滤。这就说明性能可能很差。优化方式给 city 字段加索引createindexidx_cityonuser(city);十一、看执行计划的重点顺序你以后分析执行计划可以按照这个顺序看第一步看 type重点判断有没有全表扫描。比较好的const、eq_ref、ref、range需要警惕的index、ALL尤其是ALL第二步看 key判断是否真的用到了索引。如果key NULL说明没有走索引。如果用了索引也要判断它用的是不是你预期的索引。第三步看 rows判断扫描行数大不大。rows 越大风险越高如果扫描几十行、几百行一般没太大问题。如果扫描几十万、几百万行就需要重点优化。第四步看 Extra重点关注这几个Using filesort Using temporary Using join buffer这几个通常说明 SQL 可能还有优化空间。比较好的Using index表示覆盖索引不需要回表。十二、简单记忆口诀你可以这样记type 看访问方式 key 看是否用索引 rows 看扫描多少行 Extra 看有没有额外代价。再简单一点先看 type再看 key rows 越小越好 Extra 避免 filesort 和 temporary。十三、你这段内容中有一个小点要修正你写的这句Using where一般在没有使用到索引的时候会出现。这句话不完全准确。更准确地说Using where表示 MySQL 使用 WHERE 条件进行了过滤它既可能出现在没用索引时也可能出现在用了索引之后还需要进一步过滤时。比如select*fromuserwhereage18andcity北京;如果只有age有索引MySQL 先通过age索引找到一批数据然后还要判断city 北京。这时即使用了索引也可能出现Extra Using where所以Using where本身不一定坏要结合type、key、rows一起看。十四、总结执行计划就是 MySQL 对 SQL 的执行方案说明。你真正需要重点掌握的是type访问方式判断性能好坏 key实际使用的索引 rows预计扫描行数 Extra额外执行信息其中最需要警惕的是type ALL key NULL rows 很大 Extra Using filesort Extra Using temporary Extra Using join buffer比较好的情况是type const / eq_ref / ref / range key 有值 rows 较小 Extra Using index一句话总结执行计划就是用来判断 SQL 有没有正确使用索引、扫描数据量大不大、有没有额外排序或临时表从而帮助我们定位 SQL 性能问题。

相关新闻