前言:explain(執行計劃),使用explain關鍵字能夠模擬優化器執行sql查詢語句,從而知道MySQL是如何處理sql語句。explain主要用於分析查詢語句或表結構的性能瓶頸。html
注:本系列隨筆如無特殊說明都MySQL版本都爲5.7.22。mysql
經過explain+sql語句能夠知道以下內容:sql
①表的讀取順序。(對應id)數據庫
②數據讀取操做的操做類型。(對應select_type)運維
③哪些索引可使用。(對應possible_keys)性能
④哪些索引被實際使用。(對應key)測試
⑤表直接的引用。(對應ref)優化
⑥每張表有多少行被優化器查詢。(對應rows)spa
explain使用:explain+sql語句,經過執行explain能夠得到sql語句執行的相關信息。3d
下面對explain的表頭字段含義進行解釋。
注:下圖中有些explain表頭不包含partitions和filtered字段,是由於有些截圖是直接從視頻資料中截取的,當並不影響咱們的分析。
①id
select查詢的序列號,包含一組數字,表示查詢中執行select子句或操做表的順序,該字段一般與table字段搭配來分析。
#1.id相同,執行順序從上到下。
id相同,執行順序從上到下,搭配table列進行觀察可知,執行順序爲t1->t3->t2。
#2.id不一樣,若是是子查詢,id的序號會遞增,id值越大執行優先級越高。
若是是子查詢id的序號會遞增,id值越大執行優先級越高,搭配table列可知,執行順序爲t3->t1->t2。
#3.id相同不一樣,同時存在。
id若是相同,可認爲是同一組,執行順序從上到下。在全部組中,id值越大執行優先級越高。因此執行順序爲t3->derived2(衍生表,也能夠說臨時表)->t2。
總結:id的值表示select子句或表的執行順序,id相同,執行順序從上到下,id不一樣,值越大的執行優先級越高。
②select_type
查詢的類型,主要用於區別普通查詢、聯合查詢、子查詢等複雜的查詢。其值主要有六個:
#1.SIMPLE
簡單的select查詢,查詢中不包含子查詢或union查詢。
#2.PRIMARY
查詢中若包含任何複雜的子部分,最外層查詢爲PRIMARY,也就是最後加載的就是PRIMARY。
#3.SUBQUERY
在select或where列表中包含了子查詢,就爲被標記爲SUBQUERY。
#4.DERIVED
在from列表中包含的子查詢會被標記爲DERIVED(衍生),MySQL會遞歸執行這些子查詢,將結果放在臨時表中。
#5.UNION
若第二個select出如今union後,則被標記爲UNION,若union包含在from子句的子查詢中,外層select將被標記爲DERIVED。
#6.UNION RESULT
從union表獲取結果的select。
③table
顯示sql操做屬於哪張表的。
④partitions
官方定義爲The matching partitions(匹配的分區),該字段應該是看table所在的分區吧(不曉得理解錯誤沒)。值爲NULL表示表未被分區。
⑤type
表示查詢所使用的訪問類型,type的值主要有八種,該值表示查詢的sql語句好壞,從最好到最差依次爲:system>const>eq_ref>ref>range>index>ALL。
要詳細瞭解type取值的做用,須要用數聽說話。建立tb_emp(員工表)和tb_dept(部門表)。
a)tb_emp表。
DROP TABLE IF EXISTS `tb_emp`; CREATE TABLE `tb_emp` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL, `deptid` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `idx_tb_emp_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `tb_emp`(name,deptid) VALUES ('jack', '1'); INSERT INTO `tb_emp`(name,deptid) VALUES ('tom', '1'); INSERT INTO `tb_emp`(name,deptid) VALUES ('tonny', '1'); INSERT INTO `tb_emp`(name,deptid) VALUES ('mary', '2'); INSERT INTO `tb_emp`(name,deptid) VALUES ('rose', '2'); INSERT INTO `tb_emp`(name,deptid) VALUES ('luffy', '3'); INSERT INTO `tb_emp`(name,deptid) VALUES ('outman', '4');
b)tb_dept表。
DROP TABLE IF EXISTS `tb_dept`; CREATE TABLE `tb_dept` ( `id` int(11) NOT NULL AUTO_INCREMENT, `deptname` varchar(20) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `tb_dept`(deptname) VALUES ('研發'); INSERT INTO `tb_dept`(deptname) VALUES ('測試'); INSERT INTO `tb_dept`(deptname) VALUES ('運維'); INSERT INTO `tb_dept`(deptname) VALUES ('經理');
#1.system
表只有一行記錄(等於系統表),是const的特例類型,平時不會出現,能夠忽略不計。
可是筆者發如今MySQL5.7.22時,不會出現該字段值,只能出現const,可是在MySQL5.7版本如下能夠出現該狀況。猜想MySQL5.7版本是否是進行了優化,由於system官網的解釋:
5.5.48:
5.7.22:
注:兩個引擎的執行信息不同,5.5.48執行過程當中產生了臨時表(DERIVED),5.7.22爲簡單查詢。
#2.const
表示經過一次索引就找到告終果,常出現於primary key或unique索引。由於只匹配一行數據,因此查詢很是快。如將主鍵置於where條件中,MySQL就能將查詢轉換爲一個常量。
注:對於system和const可能實際意義並非很大,由於單表單行查詢原本就快,意義不大。
#3.eq_ref
惟一索引掃描,對於每一個索引鍵,表中只有一條記錄與之匹配。常見主鍵或惟一索引掃描。
注:經理只有一人,進行了tb_dept的主鍵掃描。
#4.ref
非惟一性索引掃描,返回匹配某個單獨值的全部行。本質上也是一種索引訪問,返回匹配某值(某條件)的多行值,屬於查找和掃描的混合體。
因爲是非惟一性索引掃描,因此對tb_emp表的deptid字段建立索引:
create index idx_tb_emp_deptid on tb_emp(deptid);
#5.range
只檢索給定範圍的行,使用一個索引來檢索行,能夠在key列中查看使用的索引,通常出如今where語句的條件中,如使用between、>、<、in等查詢。
這種索引的範圍掃描比全索引掃描要好,由於索引的開始點和結束點都固定,範圍相對較小。
雖然咱們爲deptid字段建立了索引並在where中使用了between等,但在以下狀況type仍爲ALL。
對比兩圖,能夠看到使用deptid和id進行操做,其type的值一個是ALL也就是進行了全表掃描,一個是range進行了指定索引範圍值檢索。可能緣由deptid並非惟一索引。
對於以上問題,須要具體問題具體分析,並不能一律而論。
#6.index
全索引掃描,index和ALL的區別:index只遍歷索引樹,一般比ALL快,由於索引文件一般比數據文件小。雖然說index和ALL都是全表掃描,可是index是從索引中讀取,ALL是從磁盤中讀取。
#7.ALL
全表掃描。
注:通常來講,需保證查詢至少達到range級別,最好能達到ref。
⑥possible_keys和key、key_len
possible_keys:顯示可能應用在表中的索引,可能一個或多個。查詢涉及到的字段若存在索引,則該索引將被列出,但不必定被查詢實際使用。
key:實際中使用的索引,如爲NULL,則表示未使用索引。若查詢中使用了覆蓋索引,則該索引和查詢的select字段重疊。
key_len:表示索引中所使用的字節數,可經過該列計算查詢中使用的索引長度。在不損失精確性的狀況下,長度越短越好。key_len顯示的值爲索引字段的最大可能長度,並不是實際使用長度,即key_len是根據表定義計算而得,並非經過表內檢索出的。
簡單理解:possible_keys表示理論上可能用到的索引,key表示實際中使用的索引。
possible_keys爲NULL表示可能未用到索引,但key=idx_deptid表示在實際查詢的過程當中進行了索引的全掃描。
經過下面的例子來理解key_len,首先爲name字段建立索引:
create index idx_name on tb_emp(name);
注:在使用索引查詢時,當條件越精確,key_len的長度可能會越長,因此在不影響結果的狀況下,key_len的值越短越好。
⑦ref
顯示關聯的字段。若是使用常數等值查詢,則顯示const,若是是鏈接查詢,則會顯示關聯的字段。
注:因爲id相同,所以從上到下執行:
#1.tb_emp表爲非惟一性索引掃描,實際使用的索引列爲idx_name,因爲tb_emp.name='rose'爲一個常量,因此ref=const。
#2.tb_dept爲惟一索引掃描,從sql語句能夠看出,實際使用了PRIMARY主鍵索引,ref=db01.tb_emp.deptid表示關聯了db01數據庫中tb_emp表的deptid字段。
⑧rows
根據表統計信息及索引選用狀況大體估算出找到所需記錄所要讀取的行數。固然該值越小越好。
⑨filtered
百分比值,表示存儲引擎返回的數據通過濾後,剩下多少知足查詢條件記錄數量的比例。
⑩Extra
顯示十分重要的額外信息。其取值有如下幾個:
#1.Using filesort
Using filesort代表mysql會對數據使用一個外部的索引排序,而不是按照表內的索引順序進行讀取。
mysql中沒法利用索引完成的排序操做稱爲「文件排序」。
出現Using filesort就很是危險了,在數據量很是大的時候幾乎「九死一輩子」。出現Using filesort儘快優化sql語句。
deptname字段未建索引的狀況。
爲deptname字段建立索引後。
#2.Using temporary
使用了臨時表保存中間結果,常見於排序order by和分組查詢group by。很是危險,「十死無生」,急需優化。
將tb_emp中name的索引先刪除,出現以下圖結果,很是爛,Using filesort和Using temporary,「十死無生」。
爲name字段建立索引後。
#3.Using index
代表相應的select操做中使用了覆蓋索引,避免訪問表的額外數據行,效率不錯。
若是同時出現了Using where,代表索引被用來執行索引鍵值的查找。(where deptid=1)
若是沒有同時出現Using where,代表索引用來讀取數據而非執行查找動做。
刪除tb_emp表中name和deptid字段的單獨索引,建立複合索引。
從這裏給出覆蓋索引的定義:select的數據列只從索引中就能取得數據,沒必要讀取數據行。經過上面的例子理解:建立了(name,deptid)的複合索引,查詢的時候也使用複合索引或部分,這就造成了覆蓋索引。簡記:查詢使用複合索引,而且查詢的列就是索引列,不能多,個數需對應。
使用優先級Using index>Using filesort(九死一輩子)>Using temporary(十死無生)。也就說出現後面兩項代表sql語句是很是爛的,急需優化!!!
explain(執行計劃)包含的信息十分的豐富,着重關注如下幾個字段信息。
①id,select子句或表執行順序,id相同,從上到下執行,id不一樣,id值越大,執行優先級越高。
②type,type主要取值及其表示sql的好壞程度(由好到差排序):system>const>eq_ref>ref>range>index>ALL。保證range,最好到ref。
③key,實際被使用的索引列。
④ref,關聯的字段,常量等值查詢,顯示爲const,若是爲鏈接查詢,顯示關聯的字段。
⑤Extra,額外信息,使用優先級Using index>Using filesort(九死一輩子)>Using temporary(十死無生)。
着重關注上述五個字段信息,對平常生產過程當中調優十分有用。
by Shawn Chen,2018.6.22日,下午。