文章目录
- 1、id -- SQL片段的执行顺序
- 2、select_type -- 查询类型(6个)
- 2.1 、simple 简单查询
- 2.2 、primary 复杂查询,最后被执行的SQL片段
- 2.3、subquery (子查询)
- 2.4 、derived [dɪ'raɪvd] 衍生; 起源
- 2.5、union
- 2.6、union_result
- 3、type -- 索引类型 (7种)
- 4、possible_keys :
- 5、key : 实际使用的索引
- 6、key_len
- 7、ref
- 8、rows(扫描的行数)
- 9、Extra (额外信息)
- 9.1、Using filesort(用了文件排序)
- 9.2、Using index(用了索引)
- 9.3、Using temporary (用了临时表)
- 9.4、Using where
- 9.5、Using join buffer
- 9.6、 impossible where
- 9.7、 select tables optimized away
- 9.8、 distinct
- 10、实战
mysql explain 的返回项:
id , select_type , table , type ,possible_keys , key ,key_len , ref , rows ,Extra
下面我们一 一解绍
1、id – SQL片段的执行顺序
select 查询的***,包含一组数字。表示SQL片段的执行顺序。
id越大表示越先被执行 , null表示最后执行
1.1、有三种情况:
- id相同 :执行顺序由上到下;
- id不相同 :如果是子查询,id的序号会递增。id值越大,优先级越高,越先被执行。
- id有相同有不同的,同时存在 :
- id 为 null: 最后执行
1.2、示例:
1.2.1、id不相同:
先执行t3的子查询
再执行t1的子查询
最后执行t2查询
1.2.2、id相同和不同,同时存在
在所有组中,
id越大,优先级越高,越先执行;
id相同的同一组中,从上往下,按顺序执行。
2、select_type – 查询类型(6个)
查询类型 有 simple , primary ,subquery (子查询),derived (衍生), union,union_result。
主要是用于区别普通查询、联合查询、子查询等的复杂查询。
-
simple:简单的 select 查询,查询中不包含 子查询 或者 union。 -
primary:查询中若包含任何复杂的子部分,最外层查询则被标记为 primary 。 -
subquery:在 select 或 where 列表中包含了 子查询。 -
derived:在 from 列表 中包含的 子查询被标记为 derived(衍生),MySQL会递归执行这些子查询,把结果放在临时表中。 -
union:若第二个 select 出现在 union 之后,则被标记为 union;若 union 包含在from子句的子查询中,外层 select 将被标记为 derived。 -
union_result: 从 union 表合并结果的select。
2.1 、simple 简单查询
简单的select查询,查询中不包含子查询 或 union
2.2 、primary 复杂查询,最后被执行的SQL片段
查询中包含若干复杂的子部分,最外层的查询则被标记为 primary。
就是最后被执行的SQL片段。
可以理解为“鸡蛋壳”
2.3、subquery (子查询)
这个子查询是返回值的。与 derived 返回临时表,有本质的不同。
2.4 、derived [dɪ’raɪvd] 衍生; 起源
在这里表示 跟在 from 后面的 select子查询返回的临时表 。
2.5、union
若第2个 select 出现在 union 之后,则被标记为 union;
若 union 包含在 from 子询的子查询中,外层的 select 将被标记为:derived
2.6、union_result
两个 union 表查询结果的合并
3、type – 索引类型 (7种)
索引种类:all , index ,range, ref, eq_ref, constant ,system
从 最差 到 最好依次是
all < index < range < ref < eq_ref < constant < system
3.1、all (全表扫描)
表示 查询是全表扫描,性能是最差的
3.2、index (全索引扫描)
Full Index Scan 全索引扫描。 index 与 All 的区别是 index类型中遍历 索引树。 通过比All快,因为 索引文件 通常比 数据文件 小。
(也就是说虽然 all 和 Index 都是读全表,但是 index 是从索引中读取的,而 all 是从硬盘)
3.3、range
range 表示使用索引范围查询, 通过索引字段范围获取表中部分数据记录。这个类型通常出现在 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, IN() 操作中。
当 type 是 range 时, 那么 EXPLAIN 输出的 ref 字段为 NULL, 并且 key_len 字段是此次查询中使用到的索引的最长的那个.
这种范围扫描过引扫描 比 index全索引扫描 要好,因为它开始于索引的某一点,而结束于索引的另一点,不用扫描全部索引。
3.4、ref
除唯一或非主键索引外,使用普通索引,返回匹配某个单值的所有行。
本质上也是一种索引访问,它返回所有匹配某个单独值的行,
然后,它可能会找到多个符合条件的行,所以他应该属于查询和扫描的混合体。
一般情况下,能优化到ref,性能就很不错了。
3.5、eq_ref
唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描。
3.6、const :
const 用于 针对 主键 或 唯一索引 的等值查询扫描,最多只返回一行数据,所以很快。
const 查询速度非常快, 因为它只读取一次即可。
id=1 可以只接确定一个具体的值。所以是const
由于t1子查询只能查询一行记录,所以最外层执行时,type就是system。
3.7、system :
表只有一行记录,这是const类型的特例,平时不会出现,这个可以忽略不计。
4、possible_keys :
显示可以应用到这张表中的索引(就是索引名称),一个或多个;
查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被实际使用。
-
实际使用的索引,如果为NULL,则没有使用索引。(可能原因包括没有建立索引或索引失效)
-
查询中若使用了
覆盖索引(select 后要查询的字段刚好和创建的索引字段完全相同),则该索引仅出现在key列表中
创建索引:
查看possible_keys为null,key上有值。
5、key : 实际使用的索引
查询中若使用了覆盖索引,则该索引仅出现在key列表中。
如果为null,则没有使用索引。
6、key_len
key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的。
在不损失精确性的情况下,长度越短越好。
6.1 、key_len 的计算规则:
-
字符串
- char(n):n 字节长度
- varchar(n):如果是 utf8 编码, 则是( 3 n + 2)个字节;如果是 utf8mb4 编码, 则是 (4 n + 2)个字节。
-
数值类型:
- tinyint:1字节
- smallint: 2字节
- mediumint: 3字节
- int:4字节
- bigint: 8字节
-
时间类型
- date:3字节
- timestamp: 4字节
- datetime: 8字节
字段属性: null 属性 占用1个字节。如果一个字段是 not null 的, 则没有此属性。
我们来举两个简单的例子:
mysql> EXPLAIN SELECT * FROM order_info WHERE user_id < 3 AND product_name = 'p1' AND productor = 'WHH' \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: order_info
partitions: NULL
type: range
possible_keys: user_product_detail_index
key: user_product_detail_index
key_len: 9
ref: NULL
rows: 5
filtered: 11.11
Extra: Using where; Using index
1 row in set, 1 warning (0.00 sec)
上面的例子是从表 order_info 中查询指定的内容, 而我们从此表的建表语句中可以知道, 表 order_info 有一个联合索引:
KEY `user_product_detail_index` (`user_id`, `product_name`, `productor`)
不过此查询语句 where user_id < 3 AND product_name = 'p1' AND productor = 'WHH' 中,因为先进行 user_id 的范围查询, 而根据 最左前缀匹配 原则, 当遇到范围查询时, 就停止索引的匹配, 因此实际上我们使用到的索引的字段只有 user_id, 因此在 EXPLAIN 中,显示的 key_len 为 9。因为 user_id 字段是 BIGINT,占用 8 字节, 而 NULL 属性占用一个字节,因此总共是 9 个字节。
若我们将user_id 字段改为 BIGINT(20) NOT NULL DEFAULT '0' , 则 key_length 应该是8。
上面因为 最左前缀匹配原则,我们的查询仅仅使用到了联合索引的 user_id 字段, 因此效率不算高。
接下来我们来看一下下一个例子:
mysql> EXPLAIN SELECT * FROM order_info WHERE user_id = 1 AND product_name = 'p1' \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: order_info
partitions: NULL
type: ref
possible_keys: user_product_detail_index
key: user_product_detail_index
key_len: 161
ref: const,const
rows: 2
filtered: 100.00
Extra: Using index
1 row in set, 1 warning (0.00 sec)
这次的查询中, 我们没有使用到范围查询, key_len 的值为 161。为什么呢?
因为我们的查询条件 WHERE user_id = 1 AND product_name = 'p1'中,仅仅使用到了联合索引中的前两个字段, 因此 keyLen(user_id) + keyLen(product_name) = 9 +( 50 * 3 + 2) = 161。(varchar(n): 如果是 utf8 编码, 则是 3 n + 2 字节)
7、ref
这一列显示了在 key列 记录的索引中,表查找值所用到的列或常量,常见的有:const(常量),func,N
ULL,字段名(例:film.id)
显示索引的哪一列被使用了。
如果可能的话,最好是一个常数。哪些列或常量被用于查找索引列上的值。
8、rows(扫描的行数)
MySQL 查询优化器根据统计信息, 估算 SQL 要查找到结果集需要扫描读取的数据行数。
这个值非常直观显示 SQL 的效率好坏, 原则上 rows 越少越好。
注意:这个不是结果集里的行数。
9、Extra (额外信息)
EXplain 中的很多额外的信息会在 Extra 字段显示, 常见的有以下几种内容:
9.1、Using filesort(用了文件排序)
Using filesort 表示 MySQL 不能通过索引顺序达到排序效果,需额外的排序操作。
一般有 Using filesort, 都建议优化去掉, 因为这样的查询 CPU 资源消耗大。
例如下面的例子:
mysql> EXPLAIN SELECT * FROM order_info ORDER BY product_name \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: order_info
partitions: NULL
type: index
possible_keys: NULL
key: user_product_detail_index
key_len: 253
ref: NULL
rows: 9
filtered: 100.00
Extra: Using index; Using filesort
1 row in set, 1 warning (0.00 sec)
我们的索引是
KEY `user_product_detail_index` (`user_id`, `product_name`, `productor`)
但是上面的查询中根据 product_name 来排序, 因此不能使用索引进行优化, 进而会产生 Using filesort。
如果我们将排序依据改为 ORDER BY user_id, product_name , 那么就不会出现 Using filesort 了. 例如:
mysql> EXPLAIN SELECT * FROM order_info ORDER BY user_id, product_name \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: order_info
partitions: NULL
type: index
possible_keys: NULL
key: user_product_detail_index
key_len: 253
ref: NULL
rows: 9
filtered: 100.00
Extra: Using index
1 row in set, 1 warning (0.00 sec)
9.2、Using index(用了索引)
"覆盖索引扫描”, 表示查询在索引树中就可查找所需数据,不用扫描表数据文件,往往说明性能不错。
9.3、Using temporary (用了临时表)
查询有使用临时表, 一般出现于排序(order by),分组(group by)和多表 join 的情况, 查询效率不高, 建议优化。
9.4、Using where
表明使用了where过滤
9.5、Using join buffer
表明使用了连接缓存,比如说在查询的时候,多表join的次数非常多,那么将配置文件中的缓冲区的 join buffer 调大一些。
9.6、 impossible where
where 子句的值总是 false,不能用来获取任何元组
SELECT * FROM t_user WHERE id = '1' and id = '2'
9.7、 select tables optimized away
在没有 group by 子句的情况下,基于索引优化 min / max 操作或者对于MyISAM存储引擎优化 count(*) 操作,不必等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化。
9.8、 distinct
优化 distinct 操作,在找到第一匹配的元组后即停止找同样值的动作
10、实战
执行顺序按照id的顺序:4 -> 3 -> 2 -> 1 -> null
执行顺序1:id=4,select_type为UNION,说明第四个select是UNION里的第二个select,最先执行 【select name,id from t2】
执行顺序2:id=3,是整个查询中第三个select的一部分。因查询包含在from中,所以 select_type 为DERIVED ,【select id,name from t1 where other_column=”】
执行顺序3:id=2,select列表中的子查询 select_type 为 subquery,为整个查询中的第二个select 【select id from t3】
执行顺序4:id=1,表示是UNION里的第一个select,select_type 为 primary,表示该查询为外层查询,table列被标记为<derived3> ,表示查询结果来自一个衍生表,其中,derived3中的3代表该查询衍生自第三个select查询,即id为3的select。【select d1.name …..】
执行顺序5:id=null,代表从UNION的临时表中读取行的阶段,table列为< union1,4 > ,表示用第一个和第四个select的结果进行UNION操作。【两个结果union操作】