环境:
mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.13 |
+-----------+
官方文档
explai可以查看查询优化器如何决定执行查询的主要方法
1、调用explain
mysql> explain select 1\G -- desc select 1\G
*************************** 1. row *************************** -- 在查询中每个表的输入中只有一行,如果查询的时两个表的联结,那么输出将有2行
id: 1
select_type: SIMPLE --- 不包含UNION查询和子查询
table: NULL
partitions: NULL
type: NULL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: NULL
filtered: NULL
Extra: No tables used
1 row in set, 1 warning (0.00 sec)
mysql> show warnings\G
*************************** 1. row ***************************
Level: Note
Code: 1003
Message: /* select#1 */ select 1 AS `1` --->实际执行的语句:【已经被查询优化器优化了的语句】
mysql> explain format=json select 1\G
*************************** 1. row ***************************
EXPLAIN: {
"query_block": {
"select_id": 1,
"message": "No tables used"
}
}
2、explain输出
此例中用到的数据库请参考:https://blog.csdn.net/zhizhengguan/article/details/84373555 安装
类 | 含义 |
---|---|
id | 执行计划的id标志 |
select_type | SELECT的类型 |
table | 输出记录的表 |
partitions | 符合的分区,[PARTITIONS] |
type | JOIN的类型 |
possible_keys | 优化器可能使用到的索引 |
key | 优化器实际选择的索引 |
key_len | 使用索引的字节长度 |
ref | 进行比较的索引列 |
rows | 优化器预估的记录数量 |
filtered | 根据条件过滤得到的记录的百分比[EXTENDED] |
extra | 额外的显示选项 |
id
如果语句当中简单子查询、在FROM中的子查询以及UNION查询,内层的select子句将会按照从外到里顺序编号,对应于在子查询中的位置。否则,id = 1。
- 简单子查询
ysql> EXplain select (select 1 from sakila.actor limit 1) from sakila.film\G
*************************** 1. row ***************************
id: 1 --- select xx from sakila.film 是外层子查询,所以id = 1
select_type: PRIMARY
table: film ---》从表1, film中查询
partitions: NULL
type: index
possible_keys: NULL
key: idx_fk_language_id
key_len: 1
ref: NULL
rows: 1000
filtered: 100.00
Extra: Using index
*************************** 2. row *************************** -- 因为这个语句中关联到了2个表,所以有两行
id: 2 -->select 1 from sakila.actor limit 1是内层子查询,所以id=2。内层子查询先执行。
select_type: SUBQUERY
table: actor ---》从表2, actor中查询
partitions: NULL
type: index
possible_keys: NULL
key: idx_actor_last_name
key_len: 137
ref: NULL
rows: 200
filtered: 100.00
Extra: Using index
2 rows in set, 1 warning (0.00 sec)
mysql> show warnings \G
*************************** 1. row ***************************
Level: Note
Code: 1003
Message: /* select#1 */ select (/* select#2 */ select 1 from `sakila`.`actor` limit 1) AS `(select 1 from sakila.actor limit 1)` from `sakila`.`film`
因为子查询是先执行内层查询的,推测,id越大,越先执行,个人推测,等我找完资料或者验证完毕之后再来填坑
- from子句的子查询,也叫做派生表 。派生表的意思时语句执行时临时会生成一个匿名临时表,MYSQL内部通过别名在外层查询中引用这个临时表。
mysql> explain select actor_id from (select actor_id from sakila.actor LIMIT 5) as der_1\G
*************************** 1. row ***************************
id: 1
select_type: PRIMARY ---->说明有子查询或者UNION查询,而且这个是最外层的select子句
table: <derived2> --->这个就是派生表了, MYSQL内部先执行内层子查询select actor_id from sakila.actor LIMIT 5,将查询结果放入一个匿名临时表中,然后执行外层子查询时从匿名子查询中查询需要的数据
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 5
filtered: 100.00
Extra: NULL
*************************** 2. row ***************************
id: 2 -->select 1 from sakila.actor limit 1是内层子查询,所以id=2。内层子查询先执行。
select_type: DERIVED -- from 之后的SELECT子查询。
table: actor
partitions: NULL
type: index
possible_keys: NULL
key: idx_actor_last_name
key_len: 137
ref: NULL
rows: 200
filtered: 100.00
Extra: Using index
MYSQL会从内到外递归执行from之后的子查询,并将查询结果放入一个匿名临时表中,这个临时表就叫做派生表,因为该临时表是从子查询中派生来的。
- union子查询
mysql> explain SELECT actor_id from actor union SELECT first_name from actor\G
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: actor
partitions: NULL
type: index
possible_keys: NULL
key: idx_actor_last_name
key_len: 137
ref: NULL
rows: 200
filtered: 100.00
Extra: Using index
*************************** 2. row ***************************
id: 2
select_type: UNION ---》UNION后面跟着的语句
table: actor
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 200
filtered: 100.00
Extra: NULL
*************************** 3. row ***************************
id: NULL ---》这个临时表不在原SQL中出现,因此他的id列是NULL
select_type: UNION RESULT ---》用来从UNION的匿名临时表检索结果的select被标记为UNION RESULT
table: <union1,2> --》UNION结果总是将id1和id2结果放在一个匿名临时表中,之后MYSQL将结果读取到临时表之外
partitions: NULL
type: ALL -- 全表扫描
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: NULL
filtered: NULL
Extra: Using temporary ---优化器需要使用临时表
select_type
select_type | 含义 |
---|---|
SIMPLE | 简单SELECT(不使用UNION或子查询等) |
PRIMARY | 如果查询有任何复杂的子部分,则将最外层的SELECT标记为PRIMARY |
SUBQUERY | 在SELECT列表中的SELECT(不在from中的子查询) |
UNION | UNION中的第二个或后面的SELECT语句 |
DEPENDENT UNION | UNION中的第二个或后面的SELECT语句,依赖于外面的查询 |
UNION RESULT | UNION的结果 |
DEPENDENT SUBQUERY | 子查询中的第一个SELECT,依赖于外面的查询 |
DERIVED | FROM子句的SELECT |
MATERIALIZED | 物化子查询 |
UNCACHEABLE SUBQUERY | 不会被缓存的并且对于外部查询的每行都要重新计算的子查询 |
UNCACHEABLE UNION | 属于不能被缓存的 UNION中的第二个或后面的SELECT语句 |
MATERIALIZED
- 产生中间临时表(实体)
- 临时表自动创建索引并和其他表进行关联,提高性能
- 和子查询的区别式,优化器将可以进行MATERIALIZED 的语句自动
mysql> explain select film_id , (select rental_id from sakila.rental limit 1) from sakila.film AS der_2\G
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: der_2 --- sakila.film 的别名der_2
partitions: NULL
type: index -- 索引扫描
possible_keys: NULL
key: idx_fk_language_id
key_len: 1
ref: NULL
rows: 1000
filtered: 100.00
Extra: Using index
*************************** 2. row ***************************
id: 2
select_type: SUBQUERY --- 在SELECT列表中的SELECT
table: rental
partitions: NULL
type: index -- 索引扫描
possible_keys: NULL
key: idx_fk_staff_id
key_len: 1
ref: NULL
rows: 16008
filtered: 100.00
Extra: Using index
mysql> explain select film_id , (select @var1 from sakila.rental limit 1) from sakila.film AS der_2\G
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: der_2
partitions: NULL
type: index -- 索引扫描
possible_keys: NULL
key: idx_fk_language_id
key_len: 1
ref: NULL
rows: 1000
filtered: 100.00
Extra: Using index
*************************** 2. row ***************************
id: 2
select_type: UNCACHEABLE SUBQUERY -- @var1 的结果被缓存在一个ITem-cache中
table: rental
partitions: NULL
type: index -- 索引扫描
possible_keys: NULL
key: idx_fk_staff_id
key_len: 1
ref: NULL
rows: 16008
filtered: 100.00
Extra: Using index
table
- 通常是用户操作的用户表
- <unionM, N> UNION得到的结果表
- 派生表,由id=N的语句产生
- 子查询物化产生的表,由id=N的语句产生
mysql> explain select film.film_id from sakila.film inner join sakila.film_actor using(film_id) inner join sakila.actor using(actor_id);
+----+-------------+------------+
| id | select_type | table |
+----+-------------+------------+-
| 1 | SIMPLE | actor |
| 1 | SIMPLE | film_actor |
| 1 | SIMPLE | film | -- 可以从table列中从上往下查询关联优化器查询时的关联顺序:先actor -> film_actor --> film
+----+-------------+------------+-
type
依据箭头,成本从小到大
除了all之外,其他的type都可以使用到索引,除了index_merge之外,其他的type只可以用到一个索引
- ALL:按照索引次序一行一行的从头到尾扫描整张表
- index:按照索引次序扫描整张表,和全表扫描不同的是,它只扫描索引,不会扫描其他行
- 优点:不需要排序
- 缺点:如果是按随机次序访问行,开销会很大
- range:一个有限制的索引扫描–>从索引里的某一点,返回匹配这个值域的行,它不需要遍历全部索引
- 比如between,where >,in(索引),or(索引)等
- 常见于使用 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, IN()或者like等运算符的查询中。
- index_subquery:用于in形式子查询使用到了辅助索引或者in常数列表,子查询可能返回重复值,可以使用索引将子查询去重。
- unique_subquery:用于where中的in形式子查询,子查询返回不重复值唯一值
- index_merge:表示查询使用了两个以上的索引,最后取交集或者并集,常见and ,or的条件使用了不同的索引,官方排序这个在ref_or_null之后,但是实际上由于要读取所个索引,性能可能大部分时间都不如range
- ref_or_null:ref的一个变体,它意味着MYSQL必须在初次查询的结果中进行第二次查询以找出NULL条目
- fulltext:全文索引检索,要注意,全文索引的优先级很高,若全文索引和普通索引同时存在时,mysql不管代价,优先选择使用全文索引
- ref:索引查找,返回所有匹配某个单个值得行
- 缺点:可能会找到多个符合条件的行。
- 只有当使用非唯一性索引或者唯一性索引的非唯一性前缀才会发生
- 之所以叫做ref是因为索引需要和某个参考值比较。这个参考值可以是常数或者来自多表查询前一个表中的结果集
- ef_ref:最多只返回一条符合条件的记录
- const:使用唯一索引或者主键,返回记录一定是1行记录的等值where条件时,通常type是const。其他数据库也叫做唯一索引扫描
- system:表中只有一行数据或者是空表,且只能用于myisam和memory表。如果是Innodb引擎表,type列在这个情况通常都是all或者index
- NULL:MYSQL能在优化阶段分解查询语句,在执行阶段甚至用不着再访问表或者索引。
possible_keys
优化器可能使用到的索引:这个列表是优化早期创建的,因此有些索引可能会与后续优化过程是没有用的。---->揭示哪些索引有助于高效的查找
key
实际采用的索引:
extra
- Using filesort:可以使用复合索引将filesort进行优化。提高性能
- Using index:比如使用覆盖索引
- Using where: 使用where过滤条件
Extra的信息是可以作为优化的提示,但是更多的是优化器优化的一种说明 - Select tables optimized away:在没有GROUP BY子句的情况下,基于索引优化MIN/MAX操作,或者对于MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化。