EXPLAINtbl_name
或者: mysql
EXPLAIN SELECTselect_options
EXPLAIN 語句能夠被看成 DESCRIBE 的同義詞來用,也能夠用來獲取一個MySQL要執行的 SELECT 語句的相關信息。 sql
本章節主要講述了第二種 EXPLAIN 用法。 函數
在 EXPLAIN 的幫助下,您就知道何時該給表添加索引,以使用索引來查找記錄從而讓 SELECT 運行更快。 性能
若是因爲不恰當使用索引而引發一些問題的話,能夠運行 ANALYZE TABLE 來更新該表的統計信息,例如鍵的基數,它能幫您在優化方面作出更好的選擇。詳情請看"ANALYZE TABLE Syntax"。 優化
您還能夠查看優化程序是否以最佳的順序來鏈接數據表。爲了讓優化程序按照 SELECT語句中的表名的順序作鏈接,能夠在查詢的開始使用 SELECT STRAIGHT_JOIN 而不僅是SELECT。 spa
EXPLAIN 返回了一行記錄,它包括了 SELECT 語句中用到的各個表的信息。這些表在結果中按照MySQL即將執行的查詢中讀取的順序列出來。MySQL用一次掃描屢次鏈接(single-sweep, multi-join) 的方法來解決鏈接。這意味着MySQL從第一個表中讀取一條記錄,而後在第二個表中查找到對應的記錄,而後在第三個表中查找,依次類推。當全部的表都掃描完了,它輸出選擇的字段而且回溯全部的表,直到找不到爲止,由於有的表中可能有多條匹配的記錄下一條記錄將從該表讀取,再從下一個表開始繼續處理。 指針
在MySQL version 4.1中,EXPLAIN 輸出的結果格式改變了,使得它更適合例如 UNION語句、子查詢以及派生表的結構。更使人注意的是,它新增了2個字段: id 和select_type。當你使用早於MySQL 4.1的版本就看不到這些字段了。 code
EXPLAIN 結果的每行記錄顯示了每一個表的相關信息,每行記錄都包含如下幾個字段: 排序
idSELECT * FROMtbl_nameWHEREprimary_key=1; SELECT * FROMtbl_nameWHEREprimary_key_part1=1 ANDprimary_key_part2=2;eq_ref
SELECT * FROMref_table,other_tableWHEREref_table.key_column=other_table.column; SELECT * FROMref_table,other_tableWHEREref_table.key_column_part1=other_table.columnANDref_table.key_column_part2=1;ref
SELECT * FROMref_tableWHEREkey_column=expr; SELECT * FROMref_table,other_tableWHEREref_table.key_column=other_table.column; SELECT * FROMref_table,other_tableWHEREref_table.key_column_part1=other_table.columnANDref_table.key_column_part2=1;ref_or_null
SELECT * FROMref_tableWHEREkey_column=exprORkey_columnIS NULL;
valueIN (SELECTprimary_keyFROMsingle_tableWHEREsome_expr)
unique_subquery 只是用來徹底替換子查詢的索引查找函數效率更高了。 索引
index_subqueryvalueIN (SELECTkey_columnFROMsingle_tableWHEREsome_expr)range
SELECT * FROMtbl_nameWHEREkey_column= 10; SELECT * FROMtbl_nameWHEREkey_columnBETWEEN 10 and 20; SELECT * FROMtbl_nameWHEREkey_columnIN (10,20,30); SELECT * FROMtbl_nameWHEREkey_part1= 10 ANDkey_part2IN (10,20,30);index
SELECT * FROM t1 LEFT JOIN t2 ON t1.id=t2.id WHERE t2.id IS NULL;
假使 t2.id 定義爲 NOT NULL。這種狀況下,MySQL將會掃描表 t1 而且用t1.id 的值在 t2 中查找記錄。當在 t2 中找到一條匹配的記錄時,這就意味着 t2.id 確定不會都是 NULL,就不會再在 t2 中查找相同 id 值的其餘記錄了。也能夠這麼說,對於 t1 中的每一個記錄,MySQL只須要在 t2 中作一次查找,而無論在 t2 中實際有多少匹配的記錄。
range checked for each record (index map: #)你能夠經過 EXPLAIN 的結果中 rows 字段的值的乘積大概地知道本次鏈接表現如何。它能夠粗略地告訴咱們MySQL在查詢過程當中會查詢多少條記錄。若是是使用系統變量max_join_size 來取得查詢結果,這個乘積還能夠用來肯定會執行哪些多表 SELECT 語句。
下面的例子展現瞭如何經過 EXPLAIN 提供的信息來較大程度地優化多表聯合查詢的性能。
假設有下面的 SELECT 語句,正打算用 EXPLAIN 來檢測:
EXPLAIN SELECT tt.TicketNumber, tt.TimeIn, tt.ProjectReference, tt.EstimatedShipDate, tt.ActualShipDate, tt.ClientID, tt.ServiceCodes, tt.RepetitiveID, tt.CurrentProcess, tt.CurrentDPPerson, tt.RecordVolume, tt.DPPrinted, et.COUNTRY, et_1.COUNTRY, do.CUSTNAME FROM tt, et, et AS et_1, do WHERE tt.SubmitTime IS NULL AND tt.ActualPC = et.EMPLOYID AND tt.AssignedPC = et_1.EMPLOYID AND tt.ClientID = do.CUSTNMBR;
在這個例子中,先作如下假設:
Table | Column | Column Type |
tt | ActualPC | CHAR(10) |
tt | AssignedPC | CHAR(10) |
tt | ClientID | CHAR(10) |
et | EMPLOYID | CHAR(15) |
do | CUSTNMBR | CHAR(15) |
Table | Index |
tt | ActualPC |
tt | AssignedPC |
tt | ClientID |
et | EMPLOYID (primary key) |
do | CUSTNMBR (primary key) |
table type possible_keys key key_len ref rows Extra et ALL PRIMARY NULL NULL NULL 74 do ALL PRIMARY NULL NULL NULL 2135 et_1 ALL PRIMARY NULL NULL NULL 74 tt ALL AssignedPC, NULL NULL NULL 3872 ClientID, ActualPC range checked for each record (key map: 35)
因爲字段 type 的對於每一個表值都是 ALL,這個結果意味着MySQL對全部的表作一個迪卡爾積;這就是說,每條記錄的組合。這將須要花很長的時間,由於須要掃描每一個表總記錄數乘積的總和。在這狀況下,它的積是 74 * 2135 * 74 * 3872 = 45,268,558,720 條記錄。若是數據表更大的話,你能夠想象一下須要多長的時間。
在這裏有個問題是當字段定義同樣的時候,MySQL就能夠在這些字段上更快的是用索引(對 ISAM 類型的表來講,除非字段定義徹底同樣,不然不會使用索引)。在這個前提下,VARCHAR 和 CHAR是同樣的除非它們定義的長度不一致。因爲 tt.ActualPC 定義爲CHAR(10),et.EMPLOYID 定義爲 CHAR(15),兩者長度不一致。
爲了解決這個問題,須要用 ALTER TABLE 來加大 ActualPC 的長度從10到15個字符:
mysql> ALTER TABLE tt MODIFY ActualPC VARCHAR(15);
如今 tt.ActualPC 和 et.EMPLOYID 都是 VARCHAR(15)
了。再來執行一次 EXPLAIN 語句看看結果:
table type possible_keys key key_len ref rows Extra tt ALL AssignedPC, NULL NULL NULL 3872 Using ClientID, where ActualPC do ALL PRIMARY NULL NULL NULL 2135 range checked for each record (key map: 1) et_1 ALL PRIMARY NULL NULL NULL 74 range checked for each record (key map: 1) et eq_ref PRIMARY PRIMARY 15 tt.ActualPC 1
這還不夠,它還能夠作的更好:如今 rows 值乘積已經少了74倍。此次查詢須要用2秒鐘。
第二個改變是消除在比較 tt.AssignedPC = et_1.EMPLOYID 和 tt.ClientID = do.CUSTNMBR 中字段的長度不一致問題:
mysql> ALTER TABLE tt MODIFY AssignedPC VARCHAR(15), -> MODIFY ClientID VARCHAR(15);
如今 EXPLAIN 的結果以下:
table type possible_keys key key_len ref rows Extra et ALL PRIMARY NULL NULL NULL 74 tt ref AssignedPC, ActualPC 15 et.EMPLOYID 52 Using ClientID, where ActualPC et_1 eq_ref PRIMARY PRIMARY 15 tt.AssignedPC 1 do eq_ref PRIMARY PRIMARY 15 tt.ClientID 1
這看起來已是能作的最好的結果了。
遺留下來的問題是,MySQL默認地認爲字段tt.ActualPC 的值是均勻分佈的,然而表tt 並不是如此。幸虧,咱們能夠很方便的讓MySQL分析索引的分佈:
mysql>ANALYZE TABLE tt;
到此爲止,錶鏈接已經優化的很完美了,EXPLAIN 的結果以下:
table type possible_keys key key_len ref rows Extra tt ALL AssignedPC NULL NULL NULL 3872 Using ClientID, where ActualPC et eq_ref PRIMARY PRIMARY 15 tt.ActualPC 1 et_1 eq_ref PRIMARY PRIMARY 15 tt.AssignedPC 1 do eq_ref PRIMARY PRIMARY 15 tt.ClientID 1
請注意,EXPLAIN 結果中的 rows 字段的值也是MySQL的鏈接優化程序大體猜想的,請檢查這個值跟真實值是否基本一致。若是不是,能夠經過在 SELECT 語句中使用STRAIGHT_JOIN 來取得更好的性能,同時能夠試着在 FROM 分句中用不一樣的次序列出各個表。