mysql总结之explain
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 |
显示了哪些字段或者常量被用来和 |
||
rows |
估计为返回结果集而需要扫描的行。 |
不是最终结果集的函数,把所有的rows乘起来可估算出整个query需要检查的行数。有limit时会不准确。(为毛?) |
|
extra |
mysql查询的附加信息。 |
select类型:
simple:query中不包含子查询或联合查询。
primary:包含子查询或联合查询的query中,最外层的select查询。
subquery:子查询在select的目标里,不在from中,子查询的第一个select。
例如:explain select (select actor_id from actor) from film_actor
dependent subquery:子查询内层的第一个select,依赖于外部查询的结果集。
例如:explain select * from actor where actor_id in (select actor_id from film_actor)
explain select * from actor where actor_id in (select actor_id from film_actor where actor_id=1)
explain select * from actor where actor_id = (select actor_id from film_actor where actor_id=1)
uncacheable subquery:表示子查询,但返回结果不能被cache,必须依据外层查询重新计算。(在什么情况下会出现这个?)
derived:子查询在from子句中,执行查询的时候会把子查询的结果集放到临时表(衍生表)。
例如:explain select * from (select * from actor) as a。此时table列会显示<derivedn>,其中n对应id列的值。
union:在联合查询中第二个及其以后的select对应的类型。
例如:explain select * from film_actor union all select * from actor
如果union包含在一个from子查询里面,则from子查询中的第一个select标记为derived。
例如:explain select * from ( select * from film_actor union all select * from actor) a
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,同样依赖于外部查询的结果集。
例如:explain select * from actor where actor_id in (select actor_id from film_actor union all select actor_id from film_actor)
uncacheable union:表示union第二个或以后的select,但结果不能被cache,必须依据外层查询重新计算。(在什么情况下会出现)
null:mysql在优化过程中分解query,执行时甚至不用再访问表数据或者索引,比如id=-1。
例如:explain select * from actor where actor_id = -1
system:查询的表仅有一行。这是const联接类型的一个特例。(在没有任何索引的情况下,只有一条数据,myisam会显示system,innodb会显示all)
const:最多会有一条记录匹配。因为仅有一行,在这行的列值可被优化器剩余部分认为是常数。const表很快,因为它们只读取一次。发生在有一个unique key或者主键,并且where子句给它设定了一个比较值。
例如:explain select * from actor where actor_id = 1(其中actor_id是主键)
eq_ref:使用这种索引查找,最多返回一条符合条件的记录。会在使用主键或者唯一性索引访问数据时看到,除了const类型这可能是最好的联接类型。
例如:explain select * from actor, actorsex where actor.actor_id = actorsex.actor_id(其中actor_id是actor、actorsex的主键,且actorsex中只有一条记录,如果多于一条记录就不是eq_ref)
ref:这是一种索引访问。只有当使用一个非唯一性索引或者唯一性索引的非唯一性前缀(换句话说,就是无法根据该值只取得一条记录)时才会发生,将索引和某个值相比较,这个值可能是一个常数,也可能是来自前一个表里的多表查询的结果值。如果使用的键仅仅匹配少量行,该联接类型是不错的。
例如:explain select * from film_actor,actor where film_actor.actor_id=actor.actor_id and film_actor.actor_id=1
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)
index_subquery:该联接类型类似于unique_subquery,子查询中的返回结果字段组合是一个索引或索引组合,但不是一个主键或者唯一索引。
例如:explain select * from film_actor where film_id in (select film_id from film_actor)
range:在一定范围内扫描索引。如where中带有between或者>,此时ref列为null。当使用=、<>、>、>=、<、<=、is null、<=>、between或者in操作符,用常量比较关键字列时,可以使用range。
index:按索引次序扫描数据。因为按照索引扫描所以会避免排序,但也会扫描整表数据,若随机读取开销会更大。如果extra列显示using index,说明使用的是覆盖索引(覆盖索引:包含所有满足查询需要的数据列的索引)。对于innodb表特别有用,此时只访问索引数据即可,不用再根据主键信息获取原数据行,避免了二次查询,而myisam表优化效果相对innodb来说没有那么的明显。
all:按行扫描全表数据,除非查询中有limit或者extra列显示使用了distinct或notexists等限定词。
distinct:当mysql找到第一条匹配的结果值时,就停止该值的查询,然后继续该列其他值的查询。
not exists:在左连接中,优化器可以通过改变原有的查询组合而使用的优化方法。当发现一个匹配的行之后,不再为前面的行继续检索,可以部分减少数据访问的次数。例如,表t1、t2,其中t2.id为not null,对于select * from t1 left join t2 on t1.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 noticed after 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 const table:对一个有join的查询,包含一个空表或者没有数据满足一个唯一索引条件。
no tables used:查询没有from子句,或者有一个from dual(dual:虚拟表,是为了满足select...from...习惯)子句。
例如:explain select version()
range checked for each record (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不存在的一个值会显示impossible where 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 product group by name,为了排序,mysql就需要创建临时表。此时一般还会显示using filesort。
using where:表示mysql将对storage engine提取的结果进行过滤。例如,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 all databases,我试了下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,select count(state) from customer where (lname = ‘jones' and fname='john') or (state = ‘ut')。 using intersect:用两个或者两个以上的key提取数据,分别取得结果是已排序,通过求交就可以获得正确结果。例如,customer中的state和(lname,fname)是key,select count(state) from customer where (lname = ‘jones' and fname='john') and (state = ‘ut')。
using where with pushed condition:仅用在ndb上。mysql cluster用condition pushdown优化改善非索引字段和常量之间的直接比较。condition被pushed down到cluster的数据节点,并在所有数据节点同时估算,把不合条件的列剔除避免网络传输。
mysql5.1.5下latin1、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个字节。