共计 15507 个字符,预计需要花费 39 分钟才能阅读完成。
我们一般再检查一个 SQL 语句执行效率的时候,通常会首先用 explain 来看下该语句的相关情况。虽然经常使用,但时候你深刻的理解了其结果中每个字段的含义呢?其中 type 字段是我们关注的重中之重。type 字段的归纳:
连接类型
system 表只有一行
const 表最多只有一行匹配,通用用于主键或者唯一索引比较时
eq_ref 每次与之前的表合并行都只在该表读取一行,这是除了 system,const 之外最好的一种,
特点是使用 =,而且索引的所有部分都参与 join 且索引是主键或非空唯一键的索引
ref 如果每次只匹配少数行,那就是比较好的一种,使用 = 或 <=>,可以是左覆盖索引或非主键或非唯一键
fulltext 全文搜索
ref_or_null 与 ref 类似,但包括 NULL
index_merge 表示出现了索引合并优化(包括交集,并集以及交集之间的并集),但不包括跨表和全文索引。
这个比较复杂,目前的理解是合并单表的范围索引扫描(如果成本估算比普通的 range 要更优的话)
unique_subquery 在 in 子查询中,就是 value in (select…) 把形如“select unique_key_column”的子查询替换。
PS:所以不一定 in 子句中使用子查询就是低效的!
index_subquery 同上,但把形如”select non_unique_key_column“的子查询替换
range 常数值的范围
index a. 当查询是索引覆盖的,即所有数据均可从索引树获取的时候(Extra 中有 Using Index);
b. 以索引顺序从索引中查找数据行的全表扫描(无 Using Index);
c. 如果 Extra 中 Using Index 与 Using Where 同时出现的话,则是利用索引查找键值的意思;
d. 如单独出现,则是用读索引来代替读行,但不用于查找
all 全表扫描 </code>
下面是 explain 所有输出结果的详解:
一. 语法
explain < table_name >
例如: explain select * from t1 where id=888;
二.explain 输出解释
+—-+————-+——-+——-+——————-+———+———+——-+——+——-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————-+——-+——-+——————-+———+———+——-+——+——-+
1.id
我的理解是 SQL 执行的顺利的标识,SQL 从大到小的执行.
例如:
MySQL> explain select * from (select * from ( select * from t1 where id=888) a) b;
+—-+————-+————+——–+——————-+———+———+——+——+——-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————-+————+——–+——————-+———+———+——+——+——-+
| 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | |
| 2 | DERIVED | <derived3> | system | NULL | NULL | NULL | NULL | 1 | |
| 3 | DERIVED | t1 | const | PRIMARY,idx_id | PRIMARY | 4 | | 1 | |
+—-+————-+————+——–+——————-+———+———+——+——+——-+
很显然这条 SQL 是从里向外的执行, 就是从 id=3 向上执行.
2. select_type
就是 select 类型, 可以有以下几种
(1) SIMPLE
简单 SELECT(不使用 UNION 或子查询等) 例如:
mysql> explain select * from t1 where id=888;
+—-+————-+——-+——-+——————-+———+———+——-+——+——-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————-+——-+——-+——————-+———+———+——-+——+——-+
| 1 | SIMPLE | t1 | const | PRIMARY,idx_id | PRIMARY | 4 | const | 1 | |
+—-+————-+——-+——-+——————-+———+———+——-+——+——-+
(2). PRIMARY
我的理解是最外层的 select. 例如:
mysql> explain select * from (select * from t1 where id=888) a ;
+—-+————-+————+——–+——————-+———+———+——+——+——-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————-+————+——–+——————-+———+———+——+——+——-+
| 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | |
| 2 | DERIVED | t1 | const | PRIMARY,idx_id | PRIMARY | 4 | | 1 | |
+—-+————-+————+——–+——————-+———+———+——+——+——-+
(3).UNION
UNION 中的第二个或后面的 SELECT 语句. 例如
mysql> explain select * from t1 where id=888 union all select * from t1 ;
+—-+————–+————+——-+——————-+———+———+——-+——+——-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————–+————+——-+——————-+———+———+——-+——+——-+
| 1 | PRIMARY | t1 | const | PRIMARY,idx_id | PRIMARY | 4 | const | 1 | |
| 2 | UNION | t1 | ALL | NULL | NULL | NULL | NULL | 1000 | |
|NULL | UNION RESULT | <union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | |
+—-+————–+————+——-+——————-+———+———+——-+——+——-+
(4).DEPENDENT UNION
UNION 中的第二个或后面的 SELECT 语句,取决于外面的查询
mysql> explain select * from t1 where id in (select id from t1 where id=888 union all select id from t1) ;
+—-+——————–+————+——–+——————-+———+———+——-+——+————————–+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+——————–+————+——–+——————-+———+———+——-+——+————————–+
| 1 | PRIMARY | t1 | ALL | NULL | NULL | NULL | NULL | 1000 | Using where |
| 2 | DEPENDENT SUBQUERY | t1 | const | PRIMARY,idx_id | PRIMARY | 4 | const | 1 | Using index |
| 3 | DEPENDENT UNION | t1 | eq_ref | PRIMARY,idx_id | PRIMARY | 4 | func | 1 | Using where; Using index |
|NULL | UNION RESULT | <union2,3> | ALL | NULL | NULL | NULL | NULL | NULL | |
+—-+——————–+————+——–+——————-+———+———+——-+——+————————–+
(4).UNION RESULT
UNION 的结果。
mysql> explain select * from t1 where id=888 union all select * from t1 ;
+—-+————–+————+——-+——————-+———+———+——-+——+——-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————–+————+——-+——————-+———+———+——-+——+——-+
| 1 | PRIMARY | t1 | const | PRIMARY,idx_id | PRIMARY | 4 | const | 1 | |
| 2 | UNION | t1 | ALL | NULL | NULL | NULL | NULL | 1000 | |
|NULL | UNION RESULT | <union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | |
+—-+————–+————+——-+——————-+———+———+——-+——+——-+
(5).SUBQUERY
子查询中的第一个 SELECT.
mysql> explain select * from t1 where id = (select id from t1 where id=888) ;
+—-+————-+——-+——-+——————-+———+———+——-+——+————-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————-+——-+——-+——————-+———+———+——-+——+————-+
| 1 | PRIMARY | t1 | const | PRIMARY,idx_id | PRIMARY | 4 | const | 1 | |
| 2 | SUBQUERY | t1 | const | PRIMARY,idx_id | PRIMARY | 4 | | 1 | Using index |
+—-+————-+——-+——-+——————-+———+———+——-+——+————-+
(6). DEPENDENT SUBQUERY
子查询中的第一个 SELECT,取决于外面的查询
mysql> explain select id from t1 where id in (select id from t1 where id=888) ;
+—-+——————–+——-+——-+——————-+———+———+——-+——+————————–+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+——————–+——-+——-+——————-+———+———+——-+——+————————–+
| 1 | PRIMARY | t1 | index | NULL | PRIMARY | 4 | NULL | 1000 | Using where; Using index |
| 2 | DEPENDENT SUBQUERY | t1 | const | PRIMARY,idx_id | PRIMARY | 4 | const | 1 | Using index |
+—-+——————–+——-+——-+——————-+———+———+——-+——+————————–+
(7).DERIVED
派生表的 SELECT(FROM 子句的子查询)
mysql> explain select * from (select * from t1 where id=888) a ;
+—-+————-+————+——–+——————-+———+———+——+——+——-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————-+————+——–+——————-+———+———+——+——+——-+
| 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | |
| 2 | DERIVED | t1 | const | PRIMARY,idx_id | PRIMARY | 4 | | 1 | |
+—-+————-+————+——–+——————-+———+———+——+——+——-+
3.table
显示这一行的数据是关于哪张表的.
有时不是真实的表名字, 看到的是 derivedx(x 是个数字, 我的理解是第几步执行的结果)
mysql> explain select * from (select * from ( select * from t1 where id=888) a) b;
+—-+————-+————+——–+——————-+———+———+——+——+——-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————-+————+——–+——————-+———+———+——+——+——-+
| 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | |
| 2 | DERIVED | <derived3> | system | NULL | NULL | NULL | NULL | 1 | |
| 3 | DERIVED | t1 | const | PRIMARY,idx_id | PRIMARY | 4 | | 1 | |
+—-+————-+————+——–+——————-+———+———+——+——+——-+
4.type
这列很重要, 显示了连接使用了哪种类别, 有无使用索引.
从最好到最差的连接类型为 const、eq_reg、ref、range、indexhe 和 ALL
(1).system
这是 const 联接类型的一个特例。表仅有一行满足条件. 如下(t1 表上的 id 是 primary key)
mysql> explain select * from (select * from t1 where id=888) a ;
+—-+————-+————+——–+——————-+———+———+——+——+——-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————-+————+——–+——————-+———+———+——+——+——-+
| 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | |
| 2 | DERIVED | t1 | const | PRIMARY,idx_id | PRIMARY | 4 | | 1 | |
+—-+————-+————+——–+——————-+———+———+——+——+——-+
(2).const
表最多有一个匹配行,它将在查询开始时被读取。因为仅有一行,在这行的列值可被优化器剩余部分认为是常数。const 表很快,因为它们只读取一次!
const 用于用常数值比较 PRIMARY KEY 或 UNIQUE 索引的所有部分时。在下面的查询中,tbl_name 可以用于 const 表:
SELECT * from tbl_name WHERE primary_key=1;
SELECT * from tbl_name WHERE primary_key_part1= 1 和 primary_key_part2=2;
例如:
mysql> explain select * from t1 where id=888;
+—-+————-+——-+——-+——————-+———+———+——-+——+——-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————-+——-+——-+——————-+———+———+——-+——+——-+
| 1 | SIMPLE | t1 | const | PRIMARY,idx_id | PRIMARY | 4 | const | 1 | |
+—-+————-+——-+——-+——————-+———+———+——-+——+——-+
(3). eq_ref
对于每个来自于前面的表的行组合,从该表中读取一行。这可能是最好的联接类型,除了 const 类型。它用在一个索引的所有部分被联接使用并且索引是 UNIQUE 或 PRIMARY KEY。
eq_ref 可以用于使用 = 操作符比较的带索引的列。比较值可以为常量或一个使用在该表前面所读取的表的列的表达式。
在下面的例子中,MySQL 可以使用 eq_ref 联接来处理 ref_tables:
SELECT * FROM ref_table,other_table
WHERE ref_table.key_column=other_table.column;
SELECT * FROM ref_table,other_table
WHERE ref_table.key_column_part1=other_table.column
AND ref_table.key_column_part2=1;
例如
mysql> create unique index idx_id on t1(id) ;
Query OK, 1000 rows affected (0.03 sec)
Records: 1000 Duplicates: 0 Warnings: 0
mysql> explain select * from t1,t4 where t1.id=t4.accountid;
+—-+————-+——-+——–+——————-+———–+———+———————-+——+——-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————-+——-+——–+——————-+———–+———+———————-+——+——-+
| 1 | SIMPLE | t4 | ALL | NULL | NULL | NULL | NULL | 1000 | |
| 1 | SIMPLE | t1 | eq_ref | PRIMARY,idx_id | idx_id | 4 | dbatest.t4.accountid | 1 | |
+—-+————-+——-+——–+——————-+———–+———+———————-+——+——-+
(4).ref
对于每个来自于前面的表的行组合,所有有匹配索引值的行将从这张表中读取。如果联接只使用键的最左边的前缀,或如果键不是 UNIQUE 或 PRIMARY KEY(换句话说,如果联接不能基于关键字选择单个行的话),则使用 ref。如果使用的键仅仅匹配少量行,该联接类型是
不错的。
ref 可以用于使用 = 或 <=> 操作符的带索引的列。
在下面的例子中,MySQL 可以使用 ref 联接来处理 ref_tables:
SELECT * FROM ref_table WHERE key_column=expr;
SELECT * FROM ref_table,other_table
WHERE ref_table.key_column=other_table.column;
SELECT * FROM ref_table,other_table
WHERE ref_table.key_column_part1=other_table.column
AND ref_table.key_column_part2=1;
例如:
mysql> drop index idx_id on t1;
Query OK, 1000 rows affected (0.03 sec)
Records: 1000 Duplicates: 0 Warnings: 0
mysql> create index idx_id on t1(id) ;
Query OK, 1000 rows affected (0.04 sec)
Records: 1000 Duplicates: 0 Warnings: 0
mysql> explain select * from t1,t4 where t1.id=t4.accountid;
+—-+————-+——-+——+——————-+———–+———+———————-+——+——-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————-+——-+——+——————-+———–+———+———————-+——+——-+
| 1 | SIMPLE | t4 | ALL | NULL | NULL | NULL | NULL | 1000 | |
| 1 | SIMPLE | t1 | ref | PRIMARY,idx_id | idx_id | 4 | dbatest.t4.accountid | 1 | |
+—-+————-+——-+——+——————-+———–+———+———————-+——+——-+
2 rows in set (0.00 sec)
(5). ref_or_null
该联接类型如同 ref,但是添加了 MySQL 可以专门搜索包含 NULL 值的行。在解决子查询中经常使用该联接类型的优化。
在下面的例子中,MySQL 可以使用 ref_or_null 联接来处理 ref_tables:
SELECT * FROM ref_table
WHERE key_column=expr OR key_column IS NULL;
(6). index_merge
该联接类型表示使用了索引合并优化方法。在这种情况下,key 列包含了使用的索引的清单,key_len 包含了使用的索引的最长的关键元素。
例如:
mysql> explain select * from t4 where id=888 or accountid=31754306 ;
+—-+————-+——-+————-+—————————-+—————————-+———+——+——+——————————————————+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————-+——-+————-+—————————-+—————————-+———+——+——+——————————————————+
| 1 | SIMPLE | t4 | index_merge | idx_t4_id,idx_t4_accountid | idx_t4_id,idx_t4_accountid | 4,4 | NULL | 2 | Using union(idx_t4_id,idx_t4_accountid); Using where |
+—-+————-+——-+————-+—————————-+—————————-+———+——+——+——————————————————+
1 row in set (0.00 sec)
(7). unique_subquery
该类型替换了下面形式的 IN 子查询的 ref:
value IN (SELECT primary_key FROM single_table WHERE some_expr)
unique_subquery 是一个索引查找函数,可以完全替换子查询,效率更高。
(8).index_subquery
该联接类型类似于 unique_subquery。可以替换 IN 子查询,但只适合下列形式的子查询中的非唯一索引:
value IN (SELECT key_column FROM single_table WHERE some_expr)
(9).range
只检索给定范围的行,使用一个索引来选择行。key 列显示使用了哪个索引。key_len 包含所使用索引的最长关键元素。在该类型中 ref 列为 NULL。
当使用 =、<>、>、>=、<、<=、IS NULL、<=>、BETWEEN 或者 IN 操作符,用常量比较关键字列时,可以使用 range
mysql> explain select * from t1 where id=888 or id=3952603 ;
+—-+————-+——-+——-+——————-+———–+———+——+——+————-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————-+——-+——-+——————-+———–+———+——+——+————-+
| 1 | SIMPLE | t1 | range | PRIMARY,idx_id | idx_id | 4 | NULL | 2 | Using where |
+—-+————-+——-+——-+——————-+———–+———+——+——+————-+
1 row in set (0.02 sec)
(10).index
该联接类型与 ALL 相同,除了只有索引树被扫描。这通常比 ALL 快,因为索引文件通常比数据文件小。
当查询只使用作为单索引一部分的列时,MySQL 可以使用该联接类型。
(11). ALL
对于每个来自于先前的表的行组合,进行完整的表扫描。如果表是第一个没标记 const 的表,这通常不好,并且通常在它情况下很差。通常可以增加更多的索引而不要使用 ALL,使得行能基于前面的表中的常数值或列值被检索出。
5.possible_keys
possible_keys 列指出 MySQL 能使用哪个索引在该表中找到行。注意,该列完全独立于 EXPLAIN 输出所示的表的次序。这意味着在 possible_keys 中的某些键实际上不能按生成的表次序使用。
如果该列是 NULL,则没有相关的索引。在这种情况下,可以通过检查 WHERE 子句看是否它引用某些列或适合索引的列来提高你的查询性能。如果是这样,创造一个适当的索引并且再次用 EXPLAIN 检查查询
6. key
key 列显示 MySQL 实际决定使用的键(索引)。如果没有选择索引,键是 NULL。要想强制 MySQL 使用或忽视 possible_keys 列中的索引,在查询中使用 FORCE INDEX、USE INDEX 或者 IGNORE INDEX。
7.key_len
key_len 列显示 MySQL 决定使用的键长度。如果键是 NULL,则长度为 NULL。
使用的索引的长度。在不损失精确性的情况下,长度越短越好
8. ref
ref 列显示使用哪个列或常数与 key 一起从表中选择行。
9. rows
rows 列显示 MySQL 认为它执行查询时必须检查的行数。
10. Extra
该列包含 MySQL 解决查询的详细信息, 下面详细.
(1).Distinct
一旦 MYSQL 找到了与行相联合匹配的行,就不再搜索了
(2).Not exists
MYSQL 优化了 LEFT JOIN,一旦它找到了匹配 LEFT JOIN 标准的行,
就不再搜索了
(3).Range checked for each
Record(index map:#)
没有找到理想的索引,因此对于从前面表中来的每一个行组合,MYSQL 检查使用哪个索引,并用它来从表中返回行。这是使用索引的最慢的连接之一
(4).Using filesort
看到这个的时候,查询就需要优化了。MYSQL 需要进行额外的步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键值和匹配条件的全部行的行指针来排序全部行
(5).Using index
列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候
(6).Using temporary
看到这个的时候,查询需要优化了。这里,MYSQL 需要创建一个临时表来存储结果,这通常发生在对不同的列集进行 ORDER BY 上,而不是 GROUP BY 上
(7).Using where
使用了 WHERE 从句来限制哪些行将与下一张表匹配或者是返回给用户。如果不想返回表中的全部行,并且连接类型 ALL 或 index,这就会发生,或者是查询有问题。
本文永久更新链接地址:http://www.linuxidc.com/Linux/2016-12/138118.htm
![post-qrcode](https://www.xgss.net/wp-content/uploads/2023/07/二维码-微信公众号256x256.jpg)