[草稿]explain详解
[TOC]
explain 常规分析
mysql> explain select user,host from mysql.user;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| 1 | SIMPLE | user | NULL | index | NULL | PRIMARY | 276 | NULL | 18 | 100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
id
我的理解是SQL执行的顺序的标识,SQL从大到小的执行:
- 1、id相同时,执行顺序由上至下
- 2、如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
- 3、id如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行
select_type
查询中每个select子句的类型:
类型 | 描述 |
---|---|
SIMPLE | 简单SELECT,不使用UNION或子查询等 |
PRIMARY | 查询中若包含任何复杂的子部分,最外层的select被标记为PRIMARY |
UNION | UNION中的第二个或后面的SELECT语句 |
DEPENDENT UNION | UNION中的第二个或后面的SELECT语句,取决于外面的查询 |
UNION RESULT | UNION的结果 |
SUBQUERY | 子查询中的第一个SELECT |
DEPENDENT SUBQUERY | 子查询中的第一个SELECT,取决于外面的查询 |
DERIVED | 派生表的SELECT,FROM子句的子查询 |
UNCACHEABLE SUBQUERY | 一个子查询的结果不能被缓存,必须重新评估外链接的第一行 |
table
显示这一行的数据是关于哪张表的,有时不是真实的表名字,看到的是derivedx(x是个数字,我的理解是第几步执行的结果)。
mysql> explain select * from (select * from ( select * from t1 where id=2602) 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_t1_id | PRIMARY | 4 | | 1 | |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
partitions
type
表示MySQL在表中找到所需行的方式,又称“访问类型”。常用的类型有:ALL, index, range, ref, eq_ref, const, system, NULL(从左到右,性能从差到好)。
- ALL:Full Table Scan, MySQL将遍历全表以找到匹配的行
- index:Full Index Scan,index与ALL区别为index类型只遍历索引树
- range:只检索给定范围的行,使用一个索引来选择行
- ref:表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
- eq_ref: 类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件
- const、system: 当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量,system是const类型的特例,当查询的表只有一行的情况下,使用system
- NULL: MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。
possible_keys
指出MySQL能使用哪个索引在表中找到记录,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用 该列完全独立于EXPLAIN输出所示的表的次序。这意味着在possible_keys中的某些键实际上不能按生成的表次序使用。 如果该列是NULL,则没有相关的索引。在这种情况下,可以通过检查WHERE子句看是否它引用某些列或适合索引的列来提高你的查询性能。如果是这样,创造一个适当的索引并且再次用EXPLAIN检查查询
key
key列显示MySQL实际决定使用的键(索引),如果没有选择索引,键是NULL。
要想强制MySQL使用或忽视possible_keys列中的索引,在查询中使用FORCE INDEX、USE INDEX或者IGNORE INDEX,比如:
SELECT name FROM xxx force index(idx_port) WHERE port=80;
key_len
表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度(key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的)。
注:不损失精确性的情况下,长度越短越好
ref
表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值。显示索引的哪一列被使用了,如果可能的话,是一个常数。
rows
表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数。
MYSQL认为必须检查的、用来返回请求数据的行数。
filtered
待补充...
extra
关于MYSQL如何解析查询的额外信息。效率最低的是Using temporary和Using filesort,意味着MYSQL根本不能使用索引,所以检索会很慢。 该列包含MySQL解决查询的详细信息,有以下几种情况:
- Using index condition:
- Using where:列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候,表示mysql服务器将在存储引擎检索行后再进行过滤
- Using temporary:表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询
- Using filesort:MySQL中无法利用索引完成的排序操作称为“文件排序”
- Using join buffer:改值强调了在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。如果出现了这个值,那应该注意,根据查询的具体情况可能需要添加索引来改进能。
- Impossible where:这个值强调了where语句会导致没有符合条件的行。
- Select tables optimized away:这个值意味着仅通过使用索引,优化器可能仅从聚合函数结果中返回一行
总结几点
- EXPLAIN不会告诉你关于触发器、存储过程的信息或用户自定义函数对查询的影响情况
- EXPLAIN不考虑各种Cache
- EXPLAIN不能显示MySQL在执行查询时所作的优化工作
- 部分统计信息是估算的,并非精确值
- EXPALIN只能解释SELECT操作,其他操作要重写为SELECT后查看执行计划。
explain format=json 分析
EXPLAIN 命令的输出有两种不同的格式:老式的表格形式和较新的、能够提供更为细节化的、结构化的 JSON 文档。
如下所示:
mysql> explain format=json select * from risk_info limit 1;
{
"query_block": {
"select_id": 1,
"cost_info": {
"query_cost": "735.00" #.重点看这个
},
"table": {
"table_name": "risk_info",
"access_type": "ALL",
"rows_examined_per_scan": 3190,
"rows_produced_per_join": 3190,
"filtered": "100.00",
"cost_info": {
"read_cost": "97.00",
"eval_cost": "638.00",
"prefix_cost": "735.00",
"data_read_per_join": "10M"
},
"used_columns": [
"id",
"transSerialno",
"createDate",
"createTime",
"finalDate",
"finalTime",
"project_code",
"main_project_code",
"riskStage",
"riskType",
"subRiskType",
"collectType",
"dataSource",
"dataCollector",
"waitUpLevelId",
"levelId",
"riskFactor",
"indicator1",
"indicator2",
"indicator3",
"indicator4",
"derived_indicator",
"riskModule",
"riskMsg",
"riskCommFlag",
"approResult",
"status",
"appDate",
"appTime",
"lastLogId",
"lastApprovId",
"overdueStatus",
"approMsg",
"flowId",
"note1",
"note2",
"note3"
]
}
}
}
其中您需要重点查看的部分是:查询成本。查询成本是指基于查询执行的总体成本和许多不同的因素考虑,MySQL 判定一次查询所付出的花销。
一般简单查询的成本会小于 1000。介于 1000 到 100,000 的成本值被视为中等成本的查询。
因此,如果您每秒只是运行上百个(并非几万个)此类查询的话,一般速度应该比较快。如果查询成本超过 100,000 的话,那么开销就比较大了。而通常当您的系统只有单个用户时,此类查询仍然可以被迅速地执行。
当然,您需要仔细考虑一下在交互式应用程序中,使用此类查询的频率(尤其在用户数量增长的时候)。
虽然这些只是大概的数字,但是它们却能够反映出总体的规律。实际情况下,您的系统在处理查询请求负载时会表现得更好还是更糟,完全取决于自身的架构与配置。
决定查询成本的一个首要因素是:查询是否正确地使用了各种索引。如果您没有使用索引进行查询,那么会被 EXPLAIN 命令所指出来,通常源于索引是如何在数据库中被创建的,以及查询本身是如何被设计的。
这也正是为什么 EXPLAIN 值得去好好学习和使用的原因。