Mysql Explain分析

EXPLAIN 介紹

 先從一個最簡單的查詢開始:html

Query-1
explain select zipcode,firstname,lastname from people;

EXPLAIN輸出結果共有id,select_type,table,type,possible_keys,key,key_len,ref,rows和Extra幾列。mysql

id

Query-2
explain select zipcode from (select * from people a) b;

id是用來順序標識整個查詢中SELELCT 語句的,經過上面這個簡單的嵌套查詢能夠看到id越大的語句越先執行。該值可能爲NULL,若是這一行用來講明的是其餘行的聯合結果,好比UNION語句:sql

Query-3
explain select * from people where zipcode = 100000 union select * from people where zipcode = 200000;

 

select_type

SELECT語句的類型,能夠有下面幾種。服務器

SIMPLE函數

最簡單的SELECT查詢,沒有使用UNION或子查詢。見Query-1。工具

 

PRIMARY性能

在嵌套的查詢中是最外層的SELECT語句,在UNION查詢中是最前面的SELECT語句。見Query-2和Query-3。測試

 

UNION優化

UNION中第二個以及後面的SELECT語句。 見Query-3。spa

 

DERIVED

派生表SELECT語句中FROM子句中的SELECT語句。見Query-2。

 

UNION RESULT

一個UNION查詢的結果。見Query-3。

 

DEPENDENT UNION

顧名思義,首先須要知足UNION的條件,及UNION中第二個以及後面的SELECT語句,同時該語句依賴外部的查詢。

Query-4
explain select * from people where id in  (select id from people where zipcode = 100000 union select id from people where zipcode = 200000 );

Query-4中select id from people where zipcode = 200000的select_type爲DEPENDENT UNION。你也許很奇怪這條語句並無依賴外部的查詢啊。

這裏順帶說下MySQL優化器對IN操做符的優化,優化器會將IN中的uncorrelated subquery優化成一個correlated subquery(關於correlated subquery參見這裏)。

SELECT ... FROM t1 WHERE t1.a IN (SELECT b FROM t2);

相似這樣的語句會被重寫成這樣:

SELECT ... FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.b = t1.a);

因此Query-4實際上被重寫成這樣:

Query-5
explain select * from people o where exists  (select id from people where zipcode = 100000 and id = o.id union select id from people where zipcode = 200000  and id = o.id);

題外話:有時候MySQL優化器這種太過「聰明」 的作法會致使WHERE條件包含IN()的子查詢語句性能有很大損失。能夠參看《高性能MySQL第三版》6.5.1關聯子查詢一節

 

SUBQUERY

子查詢中第一個SELECT語句。

Query-6
explain select * from people  where id =  (select id from people where zipcode = 100000);

 

DEPENDENT SUBQUERY

和DEPENDENT UNION相對UNION同樣。見Query-5。

除了上述幾種常見的select_type以外還有一些其餘的這裏就不一一介紹了,不一樣MySQL版本也不盡相同。

 

table

顯示的這一行信息是關於哪一張表的。有時候並非真正的表名。

Query-7
explain select * from (select * from (select * from people a) b ) c;

能夠看到若是指定了別名就顯示的別名。

<derivedN>N就是id值,指該id值對應的那一步操做的結果。

還有<unionM,N>這種類型,出如今UNION語句中,見Query-4。

注意:MySQL對待這些表和普通表同樣,可是這些「臨時表」是沒有任何索引的。

 

type

type列很重要,是用來講明表與表之間是如何進行關聯操做的,有沒有使用索引。MySQL中「關聯」一詞比通常意義上的要寬泛,MySQL認爲任何一次查詢都是一次「關聯」,並不只僅是一個查詢須要兩張表才叫關聯,因此也能夠理解MySQL是如何訪問表的。主要有下面幾種類別。

const

當肯定最多隻會有一行匹配的時候,MySQL優化器會在查詢前讀取它並且只讀取一次,所以很是快。const只會用在將常量和主鍵或惟一索引進行比較時,並且是比較全部的索引字段。people表在id上有一個主鍵索引,在(zipcode,firstname,lastname)有一個二級索引。所以Query-8的type是const而Query-9並非:

Query-8
explain select * from people where id=1;

Query-9
explain select * from people where zipcode = 100000;

注意下面的Query-10也不能使用const table,雖然也是主鍵,也只會返回一條結果。

Query-10
explain select * from people where id >2;

 

system

這是const鏈接類型的一種特例,表僅有一行知足條件。

Query-11
explain select * from (select * from people where id = 1 )b;

<derived2>已是一個const table而且只有一條記錄。

 

eq_ref

eq_ref類型是除了const外最好的鏈接類型,它用在一個索引的全部部分被聯接使用而且索引是UNIQUE或PRIMARY KEY。

須要注意InnoDB和MyISAM引擎在這一點上有點差異。InnoDB當數據量比較小的狀況type會是All。咱們上面建立的people 和 people_car默認都是InnoDB表。

Query-12
explain select * from people a,people_car b where a.id = b.people_id;

咱們建立兩個MyISAM表people2和people_car2試試:

複製代碼

CREATE TABLE people2(
    id bigint auto_increment primary key,
    zipcode char(32) not null default '',
    address varchar(128) not null default '',
    lastname char(64) not null default '',
    firstname char(64) not null default '',
    birthdate char(10) not null default ''
)ENGINE = MyISAM;

CREATE TABLE people_car2(
    people_id bigint,
    plate_number varchar(16) not null default '',
    engine_number varchar(16) not null default '',
    lasttime timestamp
)ENGINE = MyISAM;

複製代碼

Query-13
explain select * from people2 a,people_car2 b where a.id = b.people_id;

我想這是InnoDB對性能權衡的一個結果。

eq_ref能夠用於使用 = 操做符比較的帶索引的列。比較值能夠爲常量或一個使用在該表前面所讀取的表的列的表達式。若是關聯所用的索引恰好又是主鍵,那麼就會變成更優的const了:

Query-14
explain select * from people2 a,people_car2 b where a.id = b.people_id and b.people_id = 1;

 

ref

這個類型跟eq_ref不一樣的是,它用在關聯操做只使用了索引的最左前綴,或者索引不是UNIQUE和PRIMARY KEY。ref能夠用於使用=或<=>操做符的帶索引的列。

爲了說明咱們從新創建上面的people2和people_car2表,仍然使用MyISAM可是不給id指定primary key。而後咱們分別給id和people_id創建非惟一索引。

reate index people_id on people2(id);
create index people_id on people_car2(people_id);

而後再執行下面的查詢:

Query-15
explain select * from people2 a,people_car2 b where a.id = b.people_id and a.id > 2;

Query-16
explain select * from people2 a,people_car2 b where a.id = b.people_id and a.id = 2;

Query-17
explain select * from people2 a,people_car2 b where a.id = b.people_id;

Query-18
explain select * from people2 where id = 1;

看上面的Query-15,Query-16和Query-17,Query-18咱們發現MyISAM在ref類型上的處理也是有不一樣策略的。

對於ref類型,在InnoDB上面執行上面三條語句結果徹底一致。

 

fulltext

連接是使用全文索引進行的。通常咱們用到的索引都是B樹,這裏就不舉例說明了。

 

ref_or_null

該類型和ref相似。可是MySQL會作一個額外的搜索包含NULL列的操做。在解決子查詢中常用該聯接類型的優化。(詳見這裏)。

Query-19
mysql> explain select * from people2 where id = 2 or id is null;

Query-20
explain select * from people2 where id = 2 or id is not null;

注意Query-20使用的並非ref_or_null,並且InnnoDB此次表現又不相同(數據量大的狀況下有待驗證)。

 

index_merger

該聯接類型表示使用了索引合併優化方法。在這種狀況下,key列包含了使用的索引的清單,key_len包含了使用的索引的最長的關鍵元素。關於索引合併優化看這裏

 

unique_subquery

該類型替換了下面形式的IN子查詢的ref:

value IN (SELECT primary_key FROM single_table WHERE some_expr)

unique_subquery是一個索引查找函數,能夠徹底替換子查詢,效率更高。

 

index_subquery

該聯接類型相似於unique_subquery。能夠替換IN子查詢,但只適合下列形式的子查詢中的非惟一索引:

value IN (SELECT key_column FROM single_table WHERE some_expr)

 

range

只檢索給定範圍的行,使用一個索引來選擇行。key列顯示使用了哪一個索引。key_len包含所使用索引的最長關鍵元素。在該類型中ref列爲NULL。當使用=、<>、>、>=、<、<=、IS NULL、<=>、BETWEEN或者IN操做符,用常量比較關鍵字列時,可使用range:

Query-21
explain select * from people where id = 1 or id = 2;

注意在個人測試中:發現只有id是主鍵或惟一索引時type纔會爲range。

這裏順便挑剔下MySQL使用相同的range來表示範圍查詢和列表查詢。

explain select * from people where id >1;

explain select * from people where id in (1,2);

但事實上這兩種狀況下MySQL如何使用索引是有很大差異的:

咱們不是挑剔:這兩種訪問效率是不一樣的。對於範圍條件查詢,MySQL沒法使用範圍列後面的其餘索引列了,可是對於「多個等值條件查詢」則沒有這個限制了。

——出自《高性能MySQL第三版》

 

index

該聯接類型與ALL相同,除了只有索引樹被掃描。這一般比ALL快,由於索引文件一般比數據文件小。這個類型一般的做用是告訴咱們查詢是否使用索引進行排序操做。

Query-22
explain select * from people order by id;

至於什麼狀況下MySQL會利用索引進行排序,等有時間再仔細研究。最典型的就是order by後面跟的是主鍵。

 

ALL

最慢的一種方式,即全表掃描。

 

總的來講:上面幾種鏈接類型的性能是依次遞減的(system>const),不一樣的MySQL版本、不一樣的存儲引擎甚至不一樣的數據量表現均可能不同。

 

possible_keys

possible_keys列指出MySQL能使用哪一個索引在該表中找到行。

 

key

key列顯示MySQL實際決定使用的鍵(索引)。若是沒有選擇索引,鍵是NULL。要想強制MySQL使用或忽視possible_keys列中的索引,在查詢中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。

 

key_len

key_len列顯示MySQL決定使用的鍵長度。若是鍵是NULL,則長度爲NULL。使用的索引的長度。在不損失精確性的狀況下,長度越短越好 。

 

ref

ref列顯示使用哪一個列或常數與key一塊兒從表中選擇行。

 

rows

rows列顯示MySQL認爲它執行查詢時必須檢查的行數。注意這是一個預估值。

 

Extra

Extra是EXPLAIN輸出中另一個很重要的列,該列顯示MySQL在查詢過程當中的一些詳細信息,包含的信息不少,只選擇幾個重點的介紹下。

Using filesort 

MySQL有兩種方式能夠生成有序的結果,經過排序操做或者使用索引,當Extra中出現了Using filesort 說明MySQL使用了後者,但注意雖然叫filesort但並非說明就是用了文件來進行排序,只要可能排序都是在內存裏完成的。大部分狀況下利用索引排序更快,因此通常這時也要考慮優化查詢了。

 

Using temporary

說明使用了臨時表,通常看到它說明查詢須要優化了,就算避免不了臨時表的使用也要儘可能避免硬盤臨時表的使用。

 

Not exists

MYSQL優化了LEFT JOIN,一旦它找到了匹配LEFT JOIN標準的行, 就再也不搜索了。

 

Using index 

說明查詢是覆蓋了索引的,這是好事情。MySQL直接從索引中過濾不須要的記錄並返回命中的結果。這是MySQL服務層完成的,但無需再回表查詢記錄。

 

Using index condition

這是MySQL 5.6出來的新特性,叫作「索引條件推送」。簡單說一點就是MySQL原來在索引上是不能執行如like這樣的操做的,可是如今能夠了,這樣減小了沒必要要的IO操做,可是隻能用在二級索引上,詳情點這裏

 

Using where

使用了WHERE從句來限制哪些行將與下一張表匹配或者是返回給用戶。

注意:Extra列出現Using where表示MySQL服務器將存儲引擎返回服務層之後再應用WHERE條件過濾。

 

EXPLAIN的輸出內容基本介紹完了,它還有一個擴展的命令叫作EXPLAIN EXTENDED,主要是結合SHOW WARNINGS命令能夠看到一些更多的信息。一個比較有用的是能夠看到MySQL優化器重構後的SQL。

 

Ok,EXPLAIN瞭解就到這裏,其實這些內容網上都有,只是本身實際操練下會印象更深入。下一節會介紹SHOW PROFILE、慢查詢日誌以及一些第三方工具。

相關文章
相關標籤/搜索