MariaDB社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 3122|回复: 0
打印 上一主题 下一主题

mysql总结之explain

[复制链接]
跳转到指定楼层
1#
发表于 2011-5-4 21:57:15 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 lmule 于 2011-5-4 22:03 编辑

最近看了一些explain方面的资料,但是感觉网上的都差不多,基本也和manual差不多,所以自己总结了些,有些不明白的已经用黄色底色标出,希望各位拍砖

因为有些图从word粘贴不过来,所以发附件吧 mysql之explain.zip (120 KB, 下载次数: 1352)

explain主要用于sql语句中的select查询,可以显示的查看该sql语句索引的命中情况,从而更好的利用索引、优化查询效率。

Explain
语法如下:explain[extended] select ...
其中extended是选用的,如果使用的extended,那么explain之后就可以使用show warnings查看相应的优化信息,也就是mysql内部实际执行的query。

  
列名
  

描述


说明


相关链接

  
id
  

若没有子查询和联合查询,id则都是1。


Mysql会按照id从大到小的顺序执行query,在id相同的情况下,则从上到下执行。




  
select_type
  

select类型。






  
table
  

输出的行所引用的表。


有时看到的是<derivedN>,其中N对应的是id列的值。




  
type
  

Mysql的存取方法,连接访问类型。






  
possible_keys
  

在查询过程中可能用到的索引。


在优化初期创建该列,但在以后的优化过程中会根据实际情况进行选择,所以在该列列出的索引在后续过程中可能没用。该列为NULL意味着没有相关索引,可以根据实际情况看是否需要加索引。




  
key
  

访问过程中实际用到的索引。


有可能不会出现在possible_keys中(这时可能用的是覆盖索引,即使query中没有where)。possible_keys揭示哪个索引更有效,key是优化器决定哪个索引可能最小化查询成本,查询成本基于系统开销等总和因素,有可能是“执行时间”矛盾。如果强制mysql使用或者忽略possible_keys中的索引,需要在query中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。




  
key_len
  

显示使用索引的字节数。

由根据表结构计算得出,而不是实际数据的字节数。如ColumnA(char(3)) ColumnB(int(11)),在utf-8的字符集下,key_len=3*3+4=13。计算该值时需要考虑字符列对应的字符集,不同字符集对应不同的字节数。



  
ref
  

显示了哪些字段或者常量被用来和 key
配合从表中查询记录出来。显示那些在index查询中被当作值使用的在其他表里的字段或者constants。







  
rows
  

估计为返回结果集而需要扫描的行。


不是最终结果集的函数,把所有的rows乘起来可估算出整个query需要检查的行数。有limit时会不准确。(为毛?)




  
Extra
  

mysql查询的附加信息。














select类型:
simple:query中不包含子查询或联合查询。
primary:包含子查询或联合查询的query中,最外层的select查询。
subquery:子查询在select的目标里,不在from中,子查询的第一个select。
例如:EXPLAIN SELECT (SELECT actor_id FROM actor) FROMfilm_actor
file:///C:/DOCUME%7E1/dingrui/LOCALS%7E1/Temp/msohtmlclip1/01/clip_image002.jpg
dependent subquery子查询内层的第一个select,依赖于外部查询的结果集。
例如:EXPLAINSELECT * FROM actor WHERE actor_id IN (SELECT actor_id FROM film_actor)
file:///C:/DOCUME%7E1/dingrui/LOCALS%7E1/Temp/msohtmlclip1/01/clip_image004.jpg
EXPLAIN SELECT *FROM actor WHERE actor_id IN (SELECT actor_idFROM film_actor WHERE actor_id=1)
file:///C:/DOCUME%7E1/dingrui/LOCALS%7E1/Temp/msohtmlclip1/01/clip_image006.jpg
EXPLAIN SELECT *FROM actor WHERE actor_id = (SELECT actor_idFROM film_actor WHERE actor_id=1)
file:///C:/DOCUME%7E1/dingrui/LOCALS%7E1/Temp/msohtmlclip1/01/clip_image008.jpg
uncacheable subquery:表示子查询,但返回结果不能被cache,必须依据外层查询重新计算。(在什么情况下会出现这个?)
derived:子查询在from子句中,执行查询的时候会把子查询的结果集放到临时表(衍生表)。
例如:EXPLAIN SELECT * FROM (SELECT * FROM actor) AS a。此时table列会显示<derivedN>,其中N对应id列的值。
file:///C:/DOCUME%7E1/dingrui/LOCALS%7E1/Temp/msohtmlclip1/01/clip_image010.jpg
union在联合查询中第二个及其以后的select对应的类型。
例如:EXPLAINSELECT * FROM film_actor UNION ALL SELECT * FROM actor
file:///C:/DOCUME%7E1/dingrui/LOCALS%7E1/Temp/msohtmlclip1/01/clip_image012.jpg
如果union包含在一个from子查询里面,则from子查询中的第一个select标记为derived。
例如:EXPLAINSELECT * FROM ( SELECT * FROM film_actor UNION ALL SELECT * FROM actor) a
file:///C:/DOCUME%7E1/dingrui/LOCALS%7E1/Temp/msohtmlclip1/01/clip_image014.jpg
union result从union临时表获取结果集合。例如上面两个查询结果集中的最后一行。<union1,2,...>其中1,2,...所标识的id列表代表id列,当id列表长度超过20个之后就会省略后面的<union1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,...>。例子如上图。
dependent union:子查询中的union,且为union中第二个select开始的后面所有select,同样依赖于外部查询的结果集。
例如:EXPLAINSELECT * FROM actor WHERE actor_id IN (SELECT actor_id FROM film_actor UNIONALL SELECT actor_id FROM film_actor)
file:///C:/DOCUME%7E1/dingrui/LOCALS%7E1/Temp/msohtmlclip1/01/clip_image016.jpg
uncacheable union:表示union第二个或以后的select,但结果不能被cache,必须依据外层查询重新计算。(在什么情况下会出现)









按照效率从高到低给出几种常见的type类型:
NULL:mysql在优化过程中分解query,执行时甚至不用再访问表数据或者索引,比如id=-1。
例如:EXPLAINSELECT * FROM actor WHERE actor_id = -1
file:///C:/DOCUME%7E1/dingrui/LOCALS%7E1/Temp/msohtmlclip1/01/clip_image018.jpgsystem:查询的表仅有一行。这是const联接类型的一个特例。(在没有任何索引的情况下,只有一条数据,MyISAM会显示system,InnoDB会显示ALL)
const:最多会有一条记录匹配。因为仅有一行,在这行的列值可被优化器剩余部分认为是常数。const表很快,因为它们只读取一次。发生在有一个unique key或者主键,并且where子句给它设定了一个比较值。
例如:EXPLAINSELECT * FROM actor WHERE actor_id = 1(其中actor_id是主键)
file:///C:/DOCUME%7E1/dingrui/LOCALS%7E1/Temp/msohtmlclip1/01/clip_image020.jpg
eq_ref:使用这种索引查找,最多返回一条符合条件的记录。会在使用主键或者唯一性索引访问数据时看到,除了const类型这可能是最好的联接类型。
例如:EXPLAIN SELECT * FROM actor, actorsex WHERE actor.actor_id =actorsex.actor_id(其中actor_id是actor、actorsex的主键,且actorsex中只有一条记录,如果多于一条记录就不是eq_ref)
file:///C:/DOCUME%7E1/dingrui/LOCALS%7E1/Temp/msohtmlclip1/01/clip_image022.jpg
ref:这是一种索引访问。只有当使用一个非唯一性索引或者唯一性索引的非唯一性前缀(换句话说,就是无法根据该值只取得一条记录)时才会发生,将索引和某个值相比较,这个值可能是一个常数,也可能是来自前一个表里的多表查询的结果值。如果使用的键仅仅匹配少量行,该联接类型是不错的。
例如:EXPLAINSELECT * FROM film_actor,actor
WHEREfilm_actor.actor_id=actor.actor_id AND film_actor.actor_id=1
file:///C:/DOCUME%7E1/dingrui/LOCALS%7E1/Temp/msohtmlclip1/01/clip_image024.jpg
ref_or_null类似ref。不同的是Mysql会在检索的时候额外的搜索包含 NULL 值的记录,他意味着mysql必须进行二次查找,在初次查找的结果中找出NULL条目。
index_merge:查询中使用两个或多个索引,然后对索引结果进行合并。在这种情况下,key列包含所有使用的索引,key_len包含这些索引的最长的关键元素。
select * from test where column1 = 1 or column2 = 2(没试出来!555555)
unique_subquery用来优化有子查询的in,并且该子查询是通过一个unique key选择的。子查询返回的字段组合是主键或者唯一索引。
例如:EXPLAIN SELECT* FROM actor WHERE actor_id IN (SELECT actor_id FROM actor)
file:///C:/DOCUME%7E1/dingrui/LOCALS%7E1/Temp/msohtmlclip1/01/clip_image026.jpg
index_subquery:该联接类型类似于unique_subquery,子查询中的返回结果字段组合是一个索引或索引组合,但不是一个主键或者唯一索引。
例如:EXPLAINSELECT * FROM film_actor WHERE film_id IN (SELECT film_id FROM film_actor)
file:///C:/DOCUME%7E1/dingrui/LOCALS%7E1/Temp/msohtmlclip1/01/clip_image028.jpgrange在一定范围内扫描索引。如where中带有between或者>,此时ref列为NULL。当使用=、<>、>、>=、<、<=、IS NULL、<=>、BETWEEN或者IN操作符,用常量比较关键字列时,可以使用range。
index按索引次序扫描数据。因为按照索引扫描所以会避免排序,但也会扫描整表数据,若随机读取开销会更大。如果extra列显示using index,说明使用的是覆盖索引(覆盖索引:包含所有满足查询需要的数据列的索引)。对于InnoDB表特别有用,此时只访问索引数据即可,不用再根据主键信息获取原数据行,避免了二次查询,而MyISAM表优化效果相对InnoDB来说没有那么的明显。
all按行扫描全表数据,除非查询中有limit或者extra列显示使用了distinct或notexists等限定词。








Extra信息 :
distinct:当mysql找到第一条匹配的结果值时,就停止该值的查询,然后继续该列其他值的查询。
not exists:在左连接中,优化器可以通过改变原有的查询组合而使用的优化方法。当发现一个匹配的行之后,不再为前面的行继续检索,可以部分减少数据访问的次数。例如,表t1、t2,其中t2.id为not null,对于SELECT * FROM t1 LEFT JOIN t2 ONt1.id=t2.id WHERE t2.id IS NULL;由于 t2.id非空,所以只可能是t1中有,而t2中没有,所以其结果相当于求差。left join原本是要两边join,现在Mysql优化只需要依照 t1.id在t2中找到一次t2.id即可跳出。
const row not found:涉及到的表为空表,里面没有数据。
Full scan on NULL key:是优化器对子查询的一种优化方式,无法通过索引访问NULL值的时候会做此优化。
Impossible Having:Having子句总是false而不能选择任何列。例如having 1=0
Impossible WHERE:Where子句总是false而不能选择任何列。例如where 1=0
Impossible WHERE noticed after reading const tables:mysql通过读取“const/system tables”,发现Where子句为false。也就是说:在where子句中false条件对应的表应该是const/system tables。这个并不是mysql通过统计信息做出的,而是真的去实际访问一遍数据后才得出的结论。当对某个表指定了主键或者非空唯一索引上的等值条件,一个query最多只可能命中一个结果,mysql在explain之前会优先根据这一条件查找对应记录,并用记录的实际值替换query中所有用到来自该表属性的地方。
例如:select * from a,b where a.id = 1 and b.name = a.name
执行过程如下:先根据a.id = 1找到一条记录(1, 'name1'),然后将b.name换成'name1',然后通过a.name= 'name1'查找,发现没有命中记录,最终返回“Impossible WHERE noticedafter reading const tables”。
No matching min/max row:没有行满足如下的查询条件。
例如:EXPLAIN SELECT MIN(actor_id) FROM actor WHERE actor_id > 3(只有两条记录)
actor_id为唯一性索引时,会显示“No matching min/max row”,否则会显示“using where”。
no matching row in consttable:对一个有join的查询,包含一个空表或者没有数据满足一个唯一索引条件。
No tables used:查询没有From子句,或者有一个FromDual(dual:虚拟表,是为了满足select...from...习惯)子句。
例如:EXPLAIN SELECT VERSION()
Range checked for eachrecord (index map: N):Mysql发现没有好的index,但发现如果进一步获取下一张join表的列的值后,某些index可以通过range等使用。Mysql没找到合适的可用的索引。取代的办法是,对于前一个表的每一个行连接,它会做一个检验以决定该使用哪个索引(如果有的话),并且使用这个索引来从表里取得记录。这个过程不会很快,但总比没有任何索引时做表连接来得快。
Select tables optimized away当我们使用某些聚合函数来访问存在索引的某个字段时,优化器会通过索引直接一次定位到所需要的数据行完成整个查询。在使用某些聚合函数如min,max的query,直接访问存储结构(B树或者B+树)的最左侧叶子节点或者最右侧叶子节点即可,这些可以通过index解决。Select count(*) from table(不包含where等子句),MyISAM保存了记录的总数,可以直接返回结果,而Innodb需要全表扫描。Query中不能有group by操作。
unique row not found:对于SELECT …FROM tbl_name,没有行满足unique index或者primary key。从表中查询id不存在的一个值会显示ImpossibleWHERE noticed after reading const tables。
Using filesort:指Mysql将用外部排序而不是按照index顺序排列结果。数据较少时从内存排序,否则从磁盘排序。Explain不会显示的告诉客户端用哪种排序。
Using index:表示Mysql使用覆盖索引避免全表扫描,不需要再到表中进行二次查找数据。注意不要和type中的index类型混淆。
Using index for group-by:类似Using index,所需数据只需要读取索引,当query中有group by或distinct子句时,如果分组字段也在索引中,extra就会显示该值。
Using temporary:Mysql将创建一个临时表来容纳中间结果。在group by和order by的时,如果有必要的话。例如group by一个非键列,优化器会创建一个临时表,有个按照group by条件构建的unique key,然后对于每条查询结果(忽略group by),尝试insert到临时表中,如果由于unique key导致insert失败,则已有的记录就相应的updated。例如,name上没有索引,SELECT name,COUNT(*) FROM productGROUP BY name,为了排序,Mysql就需要创建临时表。此时一般还会显示using filesort。
Using where:表示Mysql将对storageengine提取的结果进行过滤。例如,price没有index,SELECT * FROM product WHERE price=1300.00。有许多where的条件由于包含了index中的列,在查找的时候就可以过滤,所以不是所有带where子句的查询会显示Using where。
Using join buffer:5.1.18版本以后才有的值。join的返回列可以从buffer中获取,与当前表join。
例如:explain select *from t1,t2 where t1.col < 10 and t2.col < 10
Scanned N databases:指在处理information_schema查询时,有多少目录需要扫描。
例如:EXPLAIN SELECT TABLE_NAME, ROW_FORMAT FROM INFORMATION_SCHEMA.TABLES
网上说这个查询会显示Scanned alldatabases,我试了下extra列是空。
Skip_open_table,Open_frm_only, Open_trigger_only, Open_full_table:指示从information_schema查询信息时有关文件开启的优化。Skip_open_table:表信息已经获得,不需要打开。 Open_frm_only:只打开.frm文件。 Open_trigger_only:只打开.trg文件。 Open_full_table:没有优化。.frm,.myd和.myi文件都打开。
Using sort_union(…), Using union(…),Using intersect(…):都出现在index_merge读取类型中。 Using sort_union:用两个或者两个以上的key提取数据,但优化器无法确保每个key会提取到一个自然排好序的结果,所以为了排除多余的数据,需要额外的处理。例如,customer的state,(lname,fname)是key,但lname不是key,SELECT COUNT(*) FROM customer WHERE (lname = ‘Jones’) OR (state = ‘UT’),由于lname上面没有key,所以使用(lname,fname),使得结果可能不按照顺序,优化器需要额外的一些工作。 Using union:用两个或者两个以上的key提取数据,分别取得结果是已排序,通过合并就可以获得正确结果。例如,customer中的state和(lname,fname)是key,SELECTCOUNT(state) FROM customer WHERE (lname = ‘Jones’ AND fname=’John’)OR (state = ‘UT’)。 Using intersect:用两个或者两个以上的key提取数据,分别取得结果是已排序,通过求交就可以获得正确结果。例如,customer中的state和(lname,fname)是key,SELECTCOUNT(state) FROM customer WHERE (lname = ‘Jones’ AND fname=’John’)AND (state = ‘UT’)。
Using where with pushed condition:仅用在ndb上。MysqlCluster用Condition Pushdown优化改善非索引字段和常量之间的直接比较。condition被pushed down到cluster的数据节点,并在所有数据节点同时估算,把不合条件的列剔除避免网络传输。








mysql5.1.5latin1、utf8、gbk字符数、字节数、汉字的对应关系:
latin1
1character=1byte, 1汉字=2character
一个字段定义成varchar(200),可以存储100个汉字或者200个字符,占用200个字节。尤其是当字段内容是字母和汉字组成时,尽量假设字段内容都是由汉字组成,据此来设置字段长度。
utf8:
1character=3bytes, 1汉字=1character一个字段定义成varchar(200),则它可以存储200个汉字或者200个字母,占用600个字节。
gbk:
1character=2bytes,1汉字=1character一个字段定义成varchar(200),则它可以存储200个汉字或者200个字母,占用400个字节。


分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 转播转播 分享分享 分享淘帖 顶2 踩
您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|Archiver|小黑屋|手机版|MariaDB社区 ( 京ICP备07012489号    |
业务联系: QQ:48474881; 邮箱: 48474881@qq.com; 电话:13911732319
声明:本站部分文章是网友转载,若未经作者同意或署名有误,请联系网站管理员。

GMT+8, 2024-9-21 11:10 , Processed in 0.085464 second(s), 25 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表