推荐链接:
总结——》【Java】
总结——》【Mysql】
总结——》【Redis】
总结——》【Kafka】
总结——》【Spring】
总结——》【SpringBoot】
总结——》【MyBatis、MyBatis-Plus】
总结——》【Linux】
总结——》【MongoDB】
总结——》【Elasticsearch】
参考链接:官网explain-output
为了知道优化SQL语句的执行,需要查看SQL语句的具体执行过程,以加快SQL语句的执行效率。可以使用explain+SQL语句来模拟优化器执行SQL查询语句,从而知道mysql是如何处理sql语句的。
一条查询语句在经过MySQL查询优化器的各种基于成本和规则的优化会后生成一个执行计划,从而知道MySQL是如何处理SQL语句的。
这个执行计划展示了具体执行查询的方式:
- 多表连接的顺序是什么
- 对于每个表采用什么访问方法来具体执行查询
SELECT,DELETE,INSERT,REPLACE、UPDATE
输出格式 | 以哪种格式显示输出 | 是否默认 |
---|---|---|
TRADITIONAL | 表格 | 是 |
JSON | JSON | 否 |
在SQL查询的前面加上EXPLAIN关键字
-- 普通表:以表格格式显示输出
explain select * from course;
-- 普通表:以JSON格式显示输出
explain format = json select * from course;
-- 分区表:以表格格式显示输出
explain partitions select * from course;
EXPLAIN FORMAT = json select * from student where age >=3 and age <=10 and name like '%a%';
-- 3张表:course、teacher、teacher_contact
DROP TABLE IF EXISTS course;
CREATE TABLE `course` (
`cid` INT ( 3 ) DEFAULT NULL,
`cname` VARCHAR ( 20 ) DEFAULT NULL,
`tid` INT ( 3 ) DEFAULT NULL )
ENGINE = INNODB DEFAULT CHARSET = utf8mb4;
DROP TABLE IF EXISTS teacher;
CREATE TABLE `teacher` (
`tid` INT ( 3 ) DEFAULT NULL,
`tname` VARCHAR ( 20 ) DEFAULT NULL,
`tcid` INT ( 3 ) DEFAULT NULL )
ENGINE = INNODB DEFAULT CHARSET = utf8mb4;
DROP TABLE IF EXISTS teacher_contact;
CREATE TABLE `teacher_contact` (
`tcid` INT ( 3 ) DEFAULT NULL,
`phone` VARCHAR ( 200 ) DEFAULT NULL
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;
INSERT INTO `course` VALUES ( '1', 'mysql', '1' );
INSERT INTO `course` VALUES ( '2', 'jvm', '1' );
INSERT INTO `course` VALUES ( '3', 'juc', '2' );
INSERT INTO `course` VALUES ( '4', 'spring', '3' );
INSERT INTO `teacher` VALUES ( '1', 'bobo', '1' );
INSERT INTO `teacher` VALUES ( '2', 'jim', '2' );
INSERT INTO `teacher` VALUES ( '3', 'dahai', '3' );
INSERT INTO `teacher_contact` VALUES ( '1', '13688888888' );
INSERT INTO `teacher_contact` VALUES ( '2', '18166669999' );
INSERT INTO `teacher_contact` VALUES ( '3', '17722225555' );
Column | Meaning | 中文描述 |
---|---|---|
id | The SELECT identifier |
查询序列编号 |
select_type | The SELECT type |
查询类型 |
table | The table for the output row | 访问的表名 |
partitions | The matching partitions | 匹配的分区信息 |
type | The join type | 访问的类型 system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL |
possible_keys | The possible indexes to choose | 可能用到的索引 |
key | The index actually chosen | 实际用到的索引 |
key_len | The length of the chosen key | 实际用到的索引长度(字节数) |
ref | The columns compared to the index | 索引的哪一列被使用了 |
rows | Estimate of rows to be examined | 估算扫描的行数 |
filtered | Percentage of rows filtered by table condition | 过滤 |
extra | Additional information | 额外的信息说明 |
id是一组数字,是select查询序列编号,表示查询中执行select子句或者操作表的顺序,有以下3种情况:
-- 查询课程 ID 为 2,或者联系表 ID 为 3 的老师
EXPLAIN SELECT
t.tname,
c.cname,
tc.phone
FROM
teacher t,
course c,
teacher_contact tc
WHERE
t.tid = c.tid
AND t.tcid = tc.tcid
AND ( c.cid = 2 OR tc.tcid = 3 );
-- 执行顺序:t表 -> c表 -> tc表
Q:什么情况下,id的序号会递增?
A:包含子查询的时候
-- 查询 mysql 课程的老师手机号
EXPLAIN SELECT
tc.phone
FROM
teacher_contact tc
WHERE
tcid = ( SELECT tcid FROM teacher t WHERE t.tid = ( SELECT c.tid FROM course c WHERE c.cname = 'mysql' ) );
-- 执行顺序:c表 -> t表 -> tc表
查询类型:普通查询 / 联合查询 / 子查询)
select_type Value |
Meaning | 中文描述 |
---|---|---|
SIMPLE | Simple SELECT (not using UNION or subqueries) | 简单的select查询(不包含关联查询、子查询) |
PRIMARY | Outermost SELECT | 最外层的select查询 |
UNION | Second or later SELECT statement in a UNION | UNION 中的第二个或随后的 select 查询, 不依赖于外部查询的结果集 |
DEPENDENT UNION | Second or later SELECT statement in a UNION, dependent on outer query | UNION中的第二个或随后的SELECT语句,依赖于外部查询 |
UNION RESULT | Result of a UNION. | UNION查询的结果集 |
SUBQUERY | First SELECT in subquery | 子查询中的第一个SELECT查询,不依赖于外部查询的结果集 |
DEPENDENT SUBQUERY | First SELECT in subquery, dependent on outer query | 子查询中的第一个SELECT,依赖于外部查询的结果集 |
DERIVED | Derived table | 在FROM 中包含的子查询,被标记为 DERIVED(衍生),MYSQL会递归执行这些子查询,把结果放在临时表中 |
UNCACHEABLE SUBQUERY | A subquery for which the result cannot be cached and must be re-evaluated for each row of the outer query | 结果集不能被缓存的子查询,必须重新为外层查询的每一行进行评估 |
UNCACHEABLE UNION | The second or later select in a UNION that belongs to an uncacheable subquery (see UNCACHEABLE SUBQUERY) | UNION 中的第二个或随后的 select 查询,属于不可缓存的子查询 |
简单的查询不包含以下:
- 子查询
- 关联查询union
EXPLAIN SELECT * FROM teacher;
查询中如果包含子查询,最外层查询则被标记为PRIMARY
-- 查询 mysql 课程的老师手机号
EXPLAIN SELECT
tc.phone
FROM
teacher_contact tc
WHERE
tcid = ( SELECT tcid FROM teacher t WHERE t.tid = ( SELECT c.tid FROM course c WHERE c.cname = 'mysql' ) );
-- 查询 ID 为 1 或 2 的老师教授的课程
EXPLAIN SELECT
cr.cname
FROM
( SELECT * FROM course WHERE tid = 1 UNION SELECT * FROM course WHERE tid = 2 ) cr;
如果select出现在union之后,则被标记为union
EXPLAIN SELECT * FROM course WHERE tid = 1
UNION SELECT * FROM course WHERE tid = 2;
(跟union类似)union或union all联合而成的结果会受外部表影响
表示从UNION表获取结果
示例:
EXPLAIN SELECT * FROM course WHERE tid = 1
UNION SELECT * FROM course WHERE tid = 2;
-- 代表 id=1 和 id=2 的查询存在 UNION
如果select或者where中包含子查询,则被标记为SUBQUERY
-- 查询 mysql 课程的老师手机号
EXPLAIN SELECT
tc.phone
FROM
teacher_contact tc
WHERE
tcid = ( SELECT tcid FROM teacher t WHERE t.tid = ( SELECT c.tid FROM course c WHERE c.cname = 'mysql' ) );
表示subquery子查询要受到外部表查询的影响
表示在得到最终查询结果之前会用到临时表
注意:from子句中出现的子查询,叫做派生类(衍生查询)
-- 查询 ID 为 1 或 2 的老师教授的课程
EXPLAIN SELECT
cr.cname
FROM
( SELECT * FROM course WHERE tid = 1 UNION SELECT * FROM course WHERE tid = 2 ) cr;
表示子查询结果不能被缓存
表示union查询结果不能被缓存
表示访问哪一个表 (表名 / 别名 / 临时表 / union合并结果集)
:该行指的是id值为M和N的行的并集
:该行引用id值为N的行的派生表结果
:该行引用id值为N的行的具体化子查询的结果
从实际的物理表中获取数据
示例:
表示:2 和 3 表示参与union的id (查询类型为union result)
记录将与查询匹配的分区。对于非分区表,该值为NULL。
表示访问类型(以何种方式访问数据)
效率从最好到最坏依次是:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
注意:
- 一般情况下,至少达到range级别,最好能达到ref
- ALL 和 index(查询全部索引)都需要优化。
-- 提前加上索引
ALTER TABLE course ADD PRIMARY KEY (`cid`);
ALTER TABLE course ADD INDEX `idx_tid`(`tid`);
ALTER TABLE teacher ADD PRIMARY KEY (`tid`);
ALTER TABLE teacher ADD INDEX idx_tcid (tcid);
ALTER TABLE teacher_contact ADD PRIMARY KEY (`tcid`);
只有一条数据满足条件,这是const类型的特例,平时不会出现
EXPLAIN SELECT * FROM mysql.proxies_priv;
使用主键索引 / 唯一索引,只能查到一条数据
EXPLAIN SELECT * FROM course where cid = 1;
使用唯一性索引(primary key or unique)
注意:通常出现在多表的join查询,对于前表的每一个结果,都只能匹配到后表的一行结果
EXPLAIN select t.tcid from teacher t,teacher_contact tc where t.tcid = tc.tcid;
使用非唯一性索引
EXPLAIN SELECT * FROM teacher where tcid = 3;
使用唯一索引来关联子查询,不再扫描全表
使用索引来关联子查询,不再扫描全表
使用索引,在指定范围内进行查询
Q:范围查询操作符?
A:=, <>, >, >=, <, <=, IS NULL, BETWEEN, LIKE, IN()
EXPLAIN SELECT * FROM teacher t WHERE t.tid <3;
EXPLAIN SELECT * FROM teacher t WHERE t.tid BETWEEN 1 AND 2;
EXPLAIN SELECT * FROM teacher t WHERE t.tid in (1,2);
使用全索引扫描
Q:什么情况下会使用全索引扫描?
A:
情况1:查询是覆盖索引,需要的数据在索引中就可以索取
情况2:查询使用索引进行排序
-- 覆盖索引
EXPLAIN SELECT tid FROM teacher;
-- 索引排序
EXPLAIN SELECT * FROM teacher ORDER BY tid;
使用全表扫描
EXPLAIN SELECT * FROM teacher;
可能用到的索引,但不一定被实际使用,一个 or 多个 or 空
实际使用的索引
注意:
1)如果为空,则没有使用索引
2)如果使用了覆盖索引,则该索引和查询的select字段一样
EXPLAIN SELECT tcid FROM teacher where tcid = 1;
索引中使用的字节数
注意:在不损失精度的情况下长度越短越好
Explain执行计划key_len详解
显示索引的哪一列被使用了,如果可能的话,是一个常数
explain select * from emp,dept where emp.deptno = dept.deptno and emp.deptno = 10;
MySQL 认为扫描多少行才能返回请求的数据,是一个预估值
注意:一般来说行数越少越好
explain select * from emp;
查询优化器预测有多少条记录满⾜其余的搜索条件
示例:c为驱动表,t为被驱动表,驱动表的扇出值为4 * 33.33% = 1.32,说明还要对被驱动表执行2次查询
EXPLAIN SELECT * FROM course c
LEFT JOIN teacher t ON c.tid = t.tid
WHERE
c.cname > 'mysql';
额外的信息说明
覆盖索引(直接从索引中读取数据,不需要回表)
EXPLAIN SELECT tcid FROM teacher where tcid = 1;
使用where进行条件过滤,表示存储引擎返回的记录并不是所有的都满足查询条件,需要在 server 层进行过滤(跟是否使用索引没有关系)
EXPLAIN SELECT * FROM teacher where tname = 'bobo';
无法使用索引进行排序,只能利用排序算法进行排序(跟磁盘或文件没有关系),会消耗额外的位置
EXPLAIN SELECT * FROM teacher ORDER BY tname;
使用临时表保存中间结果,查询完成后删除临时表
EXPLAIN SELECT DISTINCT(tname) FROM teacher;
EXPLAIN SELECT tname FROM teacher GROUP BY tname;
代表:where语句的结果总是false
EXPLAIN select * from teacher t where 1=2;
EXPLAIN select * from teacher t where tid = 100;