MySQL 版本:5.7.35
建立课程表、教师表、教师证表如下:
createtable course( cidint(3), cnamevarchar(20), tidint(3));createtable teacher( tidint(3), tnamevarchar(20), tcidint(3));createtable teacherCard( tcidint(3), tcdescvarchar(200));
分别添加数据如下:
insertinto coursevalues(1,'java',1);insertinto coursevalues(2,'html',1);insertinto coursevalues(3,'sql',2);insertinto coursevalues(4,'web',3);insertinto teachervalues(1,'tz',1);insertinto teachervalues(2,'tw',2);insertinto teachervalues(3,'tl',3);insertinto teacherCardvalues(1,'tzdesc');insertinto teacherCardvalues(2,'twdesc');insertinto teacherCardvalues(3,'tldesc');
什么是执行计划?简而言之,就是 SQL 在数据库中执行时的表现情况,通常用于 SQL 性能分析、优化和加锁分析等场景,执行过程会在 MySQL 查询过程中由解析器,预处理器和查询优化器共同生成。
在 MySQL 中使用 explain 关键字来查看。
它可以用来分析 SQL 语句和表结构的性能瓶颈:
在 select 语句前加上 explain
explain 的结果集:
在这里,id 实际上就代表着 sql 语句的执行顺序。
示例分析:查找教授SQL 课程的老师的描述
EXPLAINSELECT teacherCard.tcdescFROM teacherCard, teacherWHERE teacherCard.tcid= teacher.tcidAND teacher.tid=(SELECT course.tidFROM courseWHERE course.cname="sql");
从结果上看,course 表对应的 sql 语句最先执行,其后是 teacher 表,最后是 teacherCard 表。
也就是说,在执行嵌套子查询时,会先执行内层的子查询语句,再执行外层的语句。
那么为什么外层语句的执行顺序是先 teacher 再 teacherCard 表呢?
事实上,这个 select 的返回结果集是笛卡尔积。
出于对性能的考虑,MySQL 会将数据量小的表或子结果作为笛卡尔积的左域,也就是会优先查询数据量小的数据表。
具体的由MySQL 查询优化器进行选择。
select_type,显示本行是简单或复杂查询。
simple,最简单的查询,在查询中不包含子查询或者 union 交并差集等操作。
示例:查询course 表的所有数据
EXPLAINSELECT*from course;
primary,当查询语句中包含任何复杂的子部分(union 或子查询),最外层查询则被标记为 primary。
subquery,当查询语句中包含任何复杂的子部分(union 或子查询),非最外层查询则被标记为 subquery。
示例分析:查找教授SQL 课程的老师的描述
EXPLAINSELECT teacherCard.tcdescFROM teacherCard, teacherWHERE teacherCard.tcid= teacher.tcidAND teacher.tid=(SELECT course.tidFROM courseWHERE course.cname="sql");
SELECT course.tid FROM course WHERE course.cname = “sql” 为子查询语句,因此被标记为subquery ,而最外层的select 语句则是被标记为primary 。
derived,衍生查询,使用到了临时表。
derived 分为两种情形:
示例分析:
EXPLAINselect cr.cnamefrom(select*from courselimit2) cr;
可以看到最外层查询的table 列是 < derived2 > ,这表示涉及到了衍生表,对应的数据集为执行编码id 为2 的查询结果集。
示例分析:
EXPLAINselect cr.cnamefrom(select*from courselimit2) crunionselect cr.cnamefrom(select*from courselimit3) cr
可以看到执行计划的最后一行的select_type 是 union result ,对应的 table 列的结果是
table,查询的表名,并不一定是真实存在的表,也可能为临时表。
partitions,查询时匹配到的分区信息,对于非分区表值为 NULL,当查询的是分区表时,partitions 显示分区表命中的分区情况。
type,联接类型,显示了连接使用了哪种类别、有无使用索引,在 SQL 优化中是一个非常重要的指标。
性能从好到坏依次是:
system > const > eq_ref > ref > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
要对 type 进行优化的前提:有索引。
以下我们只介绍几种最常见的类型:
ALL,全表扫描,通常意味着 MySQL 必须从头到尾扫描整张表,去查找匹配的行的行,性能极差。
但是,如果在查询里使用了 LIMIT n,虽然 type 依然是 ALL,但是 MySQL 只需要扫描到符合条件的前 n 行数据,就会停止继续扫描。
index,按索引次序全表扫描,避免了排序的开销。
示例:
createINDEX name_indexon course(cname);EXPLAINselect cnamefrom course;
range,范围扫描,一个有限制的索引扫描。
范围扫描分为以下两种情况:
示例:
EXPLAINselect cnamefrom coursewhere cnamelike's%';
ref,索引访问,返回所有匹配索引值的数据行,每个索引可能有 0 个或多个匹配的数据行。
只有当使用非唯一性索引或者唯一性索引的非唯一性前缀时才会发生。
示例:
createINDEX tid_indexon course(tid);EXPLAINselect*from coursewhere tid=1;
eq_ref,唯一性索引,对于每个索引键的查询,只能返回唯一一行匹配的数据,不能多也不能少。
常见于唯一索引、主键索引。
示例:
altertable teachercardaddCONSTRAINT pk_tcidPRIMARYkey(tcid);altertable teacheraddCONSTRAINT uk_tciduniqueindex(tcid);EXPLAINselect t.tcidfrom teacher t,teacherCard tcwhere t.tcid= tc.tcid;
const,最多只会有一条记录匹配。只见于唯一索引和主键索引进行等值条件查询。
示例:
createUNIQUEINDEX id_indexon course(cid);EXPLAINselect*from coursewhere cid=1;
官方文档原文是:
The table has only one row (= system table). This is a special case of the const join type.
该表只有一行(=系统表)。这是 const 关联类型的特例。
示例:从系统库 mysq l的系统表 proxies_priv 里查询数据,这里的数据在Mysql 服务启动时候已经加载在内存中,不需要进行磁盘IO 。
EXPLAINSELECT*FROM`mysql`.`proxies_priv`
possible_keys,可能用到的索引,只是一种预测,不一定准。
key,实际使用到的索引。
key_len,实际使用到的索引的长度,可以用来判断复合索引中使用到的具体索引。
在不损失精确性的情况下,原则上长度越短越好。
key_len 只计算 where 条件中用到的索引长度,而排序和分组即便是用到了索引,也不会计算到 key_len 中。
key_len 索引长度的计算方式:
示例分析:
EXPLAINselect*from coursewhere cid=1;
可以看到,key_len 为5 ,由 int 的 4 个字节 + null 的 1 个字节构成。
ref,指明当前表所参照的字段或常量。
示例分析:
EXPLAINselect*from coursewhere cid=1;
可以看到ref 这一列的值是const,因为cid 索引的条件值是一个常量。
EXPLAINselect t.tcidfrom teacher t,teacherCard tcwhere t.tcid= tc.tcid;
可以看到,tc 表的ref 值是testmysql.t.tcid,表示tc.tcid 的参照字段是testmysql 数据库的 t 表的 tcid 列。
rows,实际通过索引查询到的数据行数。
filtered,命中率,表里符合条件的记录数所占百分比。
Extra,额外的补充信息,对SQL 优化有重要作用。
常见类型:
using filesort,需要一次额外的排序,常见于order by 或 group by 没有命中索引时。需要进行优化。
示例分析:
EXPLAINselect*from coursewhere cname='sql'orderby tid;
对于单列索引,where 条件的字段与order by 的字段不一致时,会产生Using filesort。
对于复合索引,如果不遵循最左前缀原则,也会产生Using filesort 。
dropindex id_indexon course;dropindex name_indexon course;dropindex tid_indexon course;createINDEX id_name__tid_indexon course(cid,cname,tid);EXPLAINselect*from coursewhere cname='sql'orderby tid;
Using temporary,用临时表保存中间结果,常用于order by 或 group by 操作中。需要进行优化。
产生条件:
示例:
explainselect tidfrom teachergroupby tid;
参考视频:SQL优化(MySQL版)
注意:视频内容不一定匹配当前数据库版本。
暂时先到这里吧,后续需要补充的话,再在文末进行添加。
我是陈冰安,Java 工程师,时不时也会整一整Linux 。
欢迎关注我的公众号【暗星涌动】,愿与你一同进步。