EXPLAIN 陳述式提供關於 MySQL 如何執行陳述式的資訊。EXPLAIN 適用於 SELECT、DELETE、INSERT、REPLACE 和 UPDATE 陳述式。
EXPLAIN 會針對 SELECT 陳述式中使用的每個資料表傳回一列資訊。它會在輸出中依 MySQL 在處理陳述式時讀取資料表的順序來列出資料表。這表示 MySQL 會從第一個資料表讀取一列,然後在第二個資料表中找到符合的資料列,然後在第三個資料表中找到,依此類推。當所有資料表都處理完畢後,MySQL 會輸出選取的資料行,並回溯資料表清單,直到找到一個有更多符合資料列的資料表。會從這個資料表讀取下一列,並繼續處理下一個資料表。
MySQL Workbench 具有 Visual Explain 功能,可提供 EXPLAIN 輸出的視覺化呈現。請參閱 教學:使用 Explain 來改善查詢效能。
本節說明 EXPLAIN 產生的輸出資料行。後面的章節提供關於 type 和 Extra 資料行的其他資訊。
來自 EXPLAIN 的每個輸出資料列都提供關於一個資料表的資訊。每個資料列都包含 表 10.1,「EXPLAIN 輸出資料行」中摘要的值,並在資料表後面詳細說明。資料行名稱顯示在資料表的第一個資料行中;第二個資料行提供當使用 FORMAT=JSON 時在輸出中顯示的等效屬性名稱。
表 10.1 EXPLAIN 輸出欄位
| 欄位 | JSON 名稱 | 意義 |
|---|---|---|
id |
select_id |
SELECT 識別碼 |
select_type |
無 | SELECT 類型 |
table |
table_name |
輸出列的資料表 |
partitions |
partitions |
符合的分區 |
type |
access_type |
聯結類型 |
possible_keys |
possible_keys |
可選擇的索引 |
key |
key |
實際選擇的索引 |
key_len |
key_length |
選擇的索引長度 |
ref |
ref |
與索引比較的欄位 |
rows |
rows |
預計要檢查的列數 |
filtered |
filtered |
資料表條件篩選的列百分比 |
Extra |
無 | 額外資訊 |
JSON 格式的 EXPLAIN 輸出中不會顯示 NULL 的 JSON 屬性。
SELECT識別碼。這是查詢中SELECT的循序編號。如果該列是指其他列的聯集結果,則該值可能為NULL。在此情況下,table欄會顯示類似<union的值,以表示該列是指M,N>id值為M和N的列的聯集。SELECT的類型,可以是下表中顯示的任何一種。JSON 格式的EXPLAIN會將SELECT類型公開為query_block的屬性,除非它是SIMPLE或PRIMARY。JSON 名稱 (如果適用) 也會顯示在表格中。select_type值JSON 名稱 意義 SIMPLE無 簡單的 SELECT(未使用UNION或子查詢)PRIMARY無 最外層的 SELECTUNION無 UNION中第二個或後續的SELECT陳述式DEPENDENT UNIONdependent(true)UNION中第二個或後續的SELECT陳述式,依賴於外部查詢UNION RESULTunion_resultUNION的結果。SUBQUERY無 子查詢中的第一個 SELECTDEPENDENT SUBQUERYdependent(true)子查詢中的第一個 SELECT,依賴於外部查詢DERIVED無 衍生資料表 DEPENDENT DERIVEDdependent(true)依賴於另一個資料表的衍生資料表 MATERIALIZEDmaterialized_from_subquery具體化的子查詢 UNCACHEABLE SUBQUERYcacheable(false)結果無法快取,必須針對外部查詢的每一列重新評估的子查詢 UNCACHEABLE UNIONcacheable(false)UNION中屬於不可快取子查詢的第二個或後續的 select (請參閱UNCACHEABLE SUBQUERY)DEPENDENT通常表示使用相關子查詢。請參閱第 15.2.15.7 節「相關子查詢」。DEPENDENT SUBQUERY的評估與UNCACHEABLE SUBQUERY的評估不同。對於DEPENDENT SUBQUERY,子查詢僅針對其外部內容中變數的每一組不同值重新評估一次。對於UNCACHEABLE SUBQUERY,子查詢會針對外部內容的每一列重新評估。當您使用
EXPLAIN指定FORMAT=JSON時,輸出沒有直接對應於select_type的單一屬性;query_block屬性對應於給定的SELECT。大部分剛才顯示的SELECT子查詢類型對應的屬性都可用 (例如MATERIALIZED的materialized_from_subquery),並會在適當時顯示。沒有SIMPLE或PRIMARY的 JSON 對應項目。非
SELECT陳述式的select_type值會顯示受影響資料表的陳述式類型。例如,對於DELETE陳述式,select_type為DELETE。輸出列所指的資料表名稱。這也可以是下列其中一個值
<union:該列是指M,N>id值為M和N的列的聯集。<derived:該列是指N>id值為N的列的衍生資料表結果。例如,衍生資料表可能是來自FROM子句中的子查詢。<subquery:該列是指N>id值為N的列的具體化子查詢結果。請參閱第 10.2.2.2 節「使用具體化最佳化子查詢」。
partitions(JSON 名稱:partitions)查詢會從中比對記錄的分區。對於非分割資料表,值為
NULL。請參閱第 26.3.5 節「取得有關分區的資訊」。聯結類型。如需不同類型的描述,請參閱
EXPLAIN聯結類型。possible_keys(JSON 名稱:possible_keys)possible_keys欄位指出 MySQL 可從中選擇以在該資料表中尋找列的索引。請注意,此欄與EXPLAIN輸出中顯示的資料表順序完全無關。這表示possible_keys中的某些索引在實際產生的資料表順序中可能無法使用。如果此欄為
NULL(或在 JSON 格式的輸出中未定義),則表示沒有相關的索引。在此情況下,您可以檢查WHERE子句是否參考某些適用於建立索引的欄位,藉此改善查詢的效能。如果有的話,請建立適當的索引,並再次使用EXPLAIN檢查查詢。請參閱第 15.1.9 節「ALTER TABLE 陳述式」。若要查看資料表有哪些索引,請使用
SHOW INDEX FROM。tbl_namekey欄位指出 MySQL 實際決定使用的索引鍵 (索引)。如果 MySQL 決定使用possible_keys索引之一來尋找列,則該索引會列為索引鍵值。key可能會命名不在possible_keys值中的索引。如果沒有任何possible_keys索引適用於尋找列,但查詢選取的所有欄都是其他一些索引的欄,就可能會發生這種情況。也就是說,已命名的索引涵蓋選取的欄,因此雖然它不被用於決定要擷取的列,但索引掃描比資料列掃描更有效率。對於
InnoDB,即使查詢也選取主索引鍵,次要索引也可能涵蓋選取的欄,因為InnoDB會將主索引鍵值與每個次要索引一起儲存。如果key為NULL,則表示 MySQL 找不到任何可更有效率地執行查詢的索引。若要強制 MySQL 使用或忽略
possible_keys欄中列出的索引,請在查詢中使用FORCE INDEX、USE INDEX或IGNORE INDEX。請參閱第 10.9.4 節「索引提示」。對於
MyISAM資料表,執行ANALYZE TABLE有助於最佳化工具選擇更好的索引。對於MyISAM資料表,myisamchk --analyze 的作用相同。請參閱第 15.7.3.1 節「ANALYZE TABLE 陳述式」和第 9.6 節「MyISAM 資料表維護和損毀復原」。key_len欄位指出 MySQL 決定使用的索引鍵長度。key_len的值可讓您判斷 MySQL 實際使用了多部分索引鍵的多少部分。如果key欄表示NULL,則key_len欄也會表示NULL。由於索引鍵儲存格式,可為
NULL的欄的索引鍵長度比NOT NULL欄的索引鍵長度多一。ref欄顯示與key欄中命名的索引比較以從資料表中選取列的欄或常數。如果值為
func,則使用的值是某些函式的結果。若要查看哪個函式,請在EXPLAIN之後使用SHOW WARNINGS,以查看擴充的EXPLAIN輸出。函式實際上可能是運算子,例如算術運算子。rows欄位指出 MySQL 認為它必須檢查以執行查詢的列數。對於
InnoDB資料表,此數字為估計值,可能並不總是精確。filtered欄位表示表格條件篩選的預估資料列百分比。最大值為 100,表示未發生資料列篩選。從 100 遞減的值表示篩選量增加。rows顯示預估的檢查資料列數,而rows×filtered顯示與後續表格聯結的資料列數。例如,如果rows為 1000 且filtered為 50.00 (50%),則要與後續表格聯結的資料列數為 1000 × 50% = 500。此欄位包含有關 MySQL 如何解析查詢的其他資訊。如需不同值的說明,請參閱
EXPLAIN額外資訊。沒有與
Extra欄位對應的單一 JSON 屬性;但是,此欄位中可能出現的值會以 JSON 屬性或message屬性的文字形式顯示。
EXPLAIN 輸出的 type 欄位描述表格如何聯結。在 JSON 格式的輸出中,這些會以 access_type 屬性的值找到。以下清單描述了聯結類型,從最佳類型到最差類型排序。
該表格只有一個資料列(= 系統表格)。這是
const聯結類型的特殊情況。表格最多有一個符合的資料列,在查詢開始時讀取。由於只有一個資料列,此資料列中欄位的值可以被最佳化工具的其餘部分視為常數。
const表格非常快,因為它們只讀取一次。當您將
PRIMARY KEY或UNIQUE索引的所有部分與常數值比較時,會使用const。在以下查詢中,tbl_name可以用作const表格。SELECT * FROM tbl_name WHERE primary_key=1; SELECT * FROM tbl_name WHERE primary_key_part1=1 AND primary_key_part2=2;從這個表格中讀取一個資料列,對應於先前表格的每個資料列組合。除了
system和const類型之外,這是最佳的聯結類型。當聯結使用索引的所有部分,且索引是PRIMARY KEY或UNIQUE NOT NULL索引時,會使用它。eq_ref可用於使用=運算子比較的索引欄位。比較值可以是常數或使用先前讀取的表格中的欄位的表示式。在以下範例中,MySQL 可以使用eq_ref聯結來處理ref_table。SELECT * FROM ref_table,other_table WHERE ref_table.key_column=other_table.column; SELECT * FROM ref_table,other_table WHERE ref_table.key_column_part1=other_table.column AND ref_table.key_column_part2=1;從這個表格中讀取具有匹配索引值的所有資料列,對應於先前表格的每個資料列組合。如果聯結僅使用索引的最左邊前綴,或者如果索引不是
PRIMARY KEY或UNIQUE索引(換句話說,如果聯結無法根據索引值選取單一資料列),則會使用ref。如果使用的索引僅匹配少量資料列,則這是一個良好的聯結類型。ref可用於使用=或<=>運算子比較的索引欄位。在以下範例中,MySQL 可以使用ref聯結來處理ref_table。SELECT * FROM ref_table WHERE key_column=expr; SELECT * FROM ref_table,other_table WHERE ref_table.key_column=other_table.column; SELECT * FROM ref_table,other_table WHERE ref_table.key_column_part1=other_table.column AND ref_table.key_column_part2=1;聯結是使用
FULLTEXT索引執行的。此聯結類型類似於
ref,但增加了一項功能,MySQL 會額外搜尋包含NULL值的資料列。此聯結類型最佳化最常用於解析子查詢。在以下範例中,MySQL 可以使用ref_or_null聯結來處理ref_table。SELECT * FROM ref_table WHERE key_column=expr OR key_column IS NULL;此聯結類型表示使用索引合併最佳化。在這種情況下,輸出列中的
key欄位包含所用索引的清單,而key_len包含所用索引的最長索引部分清單。如需更多資訊,請參閱第 10.2.1.3 節,「索引合併最佳化」。此類型會針對以下形式的某些
IN子查詢取代eq_ref。value IN (SELECT primary_key FROM single_table WHERE some_expr)unique_subquery只是一個索引查找函式,完全取代子查詢以提高效率。此聯結類型類似於
unique_subquery。它會取代IN子查詢,但它適用於以下形式的子查詢中的非唯一索引。value IN (SELECT key_column FROM single_table WHERE some_expr)只會使用索引選取在指定範圍內的資料列。輸出列中的
key欄位表示使用哪個索引。key_len包含使用的最長索引部分。對於此類型,ref欄位為NULL。當使用任何
=、<>、>、>=、<、<=、IS NULL、<=>、BETWEEN、LIKE或IN()運算子將索引欄位與常數比較時,可以使用range。SELECT * FROM tbl_name WHERE key_column = 10; SELECT * FROM tbl_name WHERE key_column BETWEEN 10 and 20; SELECT * FROM tbl_name WHERE key_column IN (10,20,30); SELECT * FROM tbl_name WHERE key_part1 = 10 AND key_part2 IN (10,20,30);index聯結類型與ALL相同,只是掃描索引樹。發生這種情況有兩種方式:如果索引是查詢的涵蓋索引,並且可用於滿足表格所需的所有資料,則只會掃描索引樹。在這種情況下,
Extra欄位會顯示Using index。索引掃描通常比ALL快,因為索引的大小通常小於表格資料。使用從索引讀取的方式,依索引順序查找資料列來執行完整表格掃描。
Uses index不會出現在Extra欄位中。
當查詢只使用單一索引的一部分的欄位時,MySQL 可以使用此聯結類型。
針對先前表格的每個資料列組合執行完整表格掃描。如果表格是第一個未標示為
const的表格,通常情況下這不是好事,而且在所有其他情況下通常 非常 糟糕。通常,您可以透過新增索引來避免ALL,這些索引可以根據常數值或先前表格的欄位值,從表格啟用資料列擷取。
EXPLAIN 輸出的 Extra 欄位包含有關 MySQL 如何解析查詢的其他資訊。以下清單說明此欄位中可能出現的值。每個項目也表示 JSON 格式的輸出,其中哪個屬性會顯示 Extra 值。對於其中一些,有特定的屬性。其他則顯示為 message 屬性的文字。
如果您想要讓查詢盡可能快速,請注意 Extra 欄位的值 Using filesort 和 Using temporary,或者在 JSON 格式的 EXPLAIN 輸出中,注意 using_filesort 和 using_temporary_table 屬性等於 true。
Backward index scan(JSON:backward_index_scan)最佳化工具能夠在
InnoDB表格上使用降序索引。與Using index一起顯示。如需更多資訊,請參閱第 10.3.13 節,「降序索引」。Child of '(JSON:table' pushed join@1message文字)此表格在可以向下推送到 NDB 核心的聯結中,被參考為
table的子項。僅適用於 NDB 叢集,當啟用向下推送聯結時。如需更多資訊和範例,請參閱ndb_join_pushdown伺服器系統變數的描述。const row not found(JSON 屬性:const_row_not_found)對於類似
SELECT ... FROM的查詢,該表格是空的。tbl_name刪除所有列(JSON 屬性:message)對於
DELETE,某些儲存引擎(例如MyISAM)支援一種處理方法,可以簡單快速地刪除所有資料表列。如果引擎使用此最佳化,則會顯示此Extra值。Distinct(JSON 屬性:distinct)MySQL 正在尋找不同的值,因此在找到第一個符合的列之後,它會停止為目前的列組合搜尋更多列。
FirstMatch((JSON 屬性:tbl_name)first_match)半聯結 FirstMatch 聯結捷徑策略用於
tbl_name。在 NULL 鍵上完整掃描(JSON 屬性:message)當最佳化工具無法使用索引查詢存取方法時,這會作為子查詢最佳化的回退策略發生。
不可能的 HAVING(JSON 屬性:message)HAVING子句始終為 false,無法選取任何列。不可能的 WHERE(JSON 屬性:message)WHERE子句始終為 false,無法選取任何列。讀取 const 資料表後發現不可能的 WHERE(JSON 屬性:message)LooseScan((JSON 屬性:m..n)message)使用半聯結 LooseScan 策略。
m和n是索引鍵部分編號。沒有符合的 min/max 列(JSON 屬性:message)沒有列滿足諸如
SELECT MIN(...) FROM ... WHERE之類查詢的條件。condition在 const 資料表中沒有符合的列(JSON 屬性:message)對於具有聯結的查詢,存在一個空的資料表或一個沒有列滿足唯一索引條件的資料表。
在分割區修剪後沒有符合的列(JSON 屬性:message)對於
DELETE或UPDATE,最佳化工具在分割區修剪後發現沒有要刪除或更新的內容。它的含義類似於SELECT陳述式的Impossible WHERE。沒有使用資料表(JSON 屬性:message)查詢沒有
FROM子句,或有FROM DUAL子句。對於
INSERT或REPLACE陳述式,當沒有SELECT部分時,EXPLAIN會顯示此值。例如,它會出現在EXPLAIN INSERT INTO t VALUES(10)中,因為它等效於EXPLAIN INSERT INTO t SELECT 10 FROM DUAL。不存在(JSON 屬性:message)MySQL 能夠對查詢執行
LEFT JOIN最佳化,並且在找到一個符合LEFT JOIN條件的列之後,不會針對先前的列組合檢查此資料表中的更多列。以下是可以透過這種方式最佳化的查詢類型範例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中查詢列。如果 MySQL 在t2中找到符合的列,它知道t2.id永遠不能為NULL,並且不會掃描t2中具有相同id值的其餘列。換句話說,對於t1中的每一列,無論t2中實際符合多少列,MySQL 都只需要在t2中進行單次查詢。這也可能表示形式為
NOT IN (或subquery)NOT EXISTS (的subquery)WHERE條件已在內部轉換為反聯結。這會移除子查詢,並將其資料表帶入最上層查詢的計畫中,從而提供更佳的成本規劃。透過合併半聯結和反聯結,最佳化工具可以在執行計畫中更自由地重新排序資料表,在某些情況下會產生更快的計畫。您可以透過檢查執行
EXPLAIN後SHOW WARNINGS中的Message欄位,或在EXPLAIN FORMAT=TREE的輸出中,查看是否針對給定查詢執行反聯結轉換。注意反聯結是半聯結
的補集。反聯結會從table_aJOINtable_bONconditiontable_a中傳回所有在table_b中 沒有 符合condition的列。計畫尚未準備好(JSON 屬性:無)當最佳化工具尚未完成為具名連線中執行的陳述式建立執行計畫時,此值會與
EXPLAIN FOR CONNECTION一起出現。如果執行計畫輸出包含多行,則其中任何或全部行都可能具有此Extra值,具體取決於最佳化工具在確定完整執行計畫方面的進度。為每個記錄檢查範圍 (索引圖:(JSON 屬性:N)message)MySQL 找不到要使用的良好索引,但發現可以使用前述資料表中的資料行值來使用某些索引。對於前述資料表中的每個列組合,MySQL 會檢查是否可以使用
range或index_merge存取方法來擷取列。這不是非常快,但比完全沒有索引地執行聯結更快。適用性標準如 第 10.2.1.2 節「範圍最佳化」和 第 10.2.1.3 節「索引合併最佳化」中所述,但前述資料表的所有資料行值都是已知的並被視為常數。索引從 1 開始編號,順序與資料表的
SHOW INDEX中顯示的順序相同。索引圖值N是位元遮罩值,表示哪些索引是候選索引。例如,值0x19(二進位 11001)表示會考慮索引 1、4 和 5。遞迴(JSON 屬性:recursive)這表示該列適用於遞迴通用資料表運算式的遞迴
SELECT部分。請參閱 第 15.2.20 節「WITH (通用資料表運算式)」。重新實體化(JSON 屬性:rematerialize)Rematerialize (X,...)會顯示在資料表T的EXPLAIN列中,其中X是任何橫向衍生資料表,當讀取T的新列時,會觸發其重新實體化。例如SELECT ... FROM t, LATERAL (derived table that refers to t) AS dt ...每次頂層查詢處理
t的新列時,都會重新實體化衍生資料表的內容,以使其保持最新狀態。已掃描(JSON 屬性:N個資料庫message)這表示當處理
INFORMATION_SCHEMA資料表的查詢時,伺服器執行的目錄掃描次數,如 第 10.2.3 節「最佳化 INFORMATION_SCHEMA 查詢」中所述。N的值可以是 0、1 或all。已最佳化掉選取的資料表(JSON 屬性:message)最佳化工具判斷 1) 最多應傳回一個列,以及 2) 若要產生此列,必須讀取一組確定性的列。當可以在最佳化階段讀取要讀取的列時(例如,透過讀取索引列),則無需在查詢執行期間讀取任何資料表。
當查詢隱式分組時(包含彙總函式,但沒有
GROUP BY子句),則滿足第一個條件。當每個使用的索引執行一個列查詢時,則滿足第二個條件。讀取的索引數量決定要讀取的列數。考慮以下隱式分組查詢
SELECT MIN(c1), MIN(c2) FROM t1;假設可以透過讀取一個索引列來擷取
MIN(c1),並且可以透過從不同索引讀取一個列來擷取MIN(c2)。也就是說,對於每個資料行c1和c2,都存在一個索引,其中該資料行是索引的第一個資料行。在這種情況下,會傳回一個列,該列是透過讀取兩個確定性的列而產生的。如果要讀取的列不是確定性的,則不會出現此
Extra值。請考慮這個查詢SELECT MIN(c2) FROM t1 WHERE c1 <= 10;假設
(c1, c2)是一個涵蓋索引。使用此索引,所有c1 <= 10的資料列都必須掃描,以找到最小的c2值。相反地,考慮以下查詢:SELECT MIN(c2) FROM t1 WHERE c1 = 10;在這種情況下,第一個
c1 = 10的索引列包含最小的c2值。只需要讀取一列即可產生傳回的資料列。對於每個資料表都維護精確列數的儲存引擎(例如
MyISAM,但不包括InnoDB),當WHERE子句遺失或始終為真,且沒有GROUP BY子句時,COUNT(*)查詢可能會出現此Extra值。(這是一個隱式分組查詢的實例,其中儲存引擎會影響是否可以讀取確定數量的資料列。)Skip_open_table、Open_frm_only、Open_full_table(JSON 屬性:message)這些值表示適用於
INFORMATION_SCHEMA資料表查詢的檔案開啟最佳化。Skip_open_table:不需要開啟資料表檔案。該資訊已從資料字典中取得。Open_frm_only:只需要讀取資料字典即可取得資料表資訊。Open_full_table:未最佳化的資訊查詢。必須從資料字典讀取資料表資訊,並讀取資料表檔案。
Start temporary、End temporary(JSON 屬性:message)這表示用於半聯接重複資料刪除策略的暫存資料表使用情況。
unique row not found(JSON 屬性:message)對於諸如
SELECT ... FROM的查詢,沒有任何資料列滿足資料表上tbl_nameUNIQUE索引或PRIMARY KEY的條件。Using filesort(JSON 屬性:using_filesort)MySQL 必須執行額外步驟來找出如何以排序順序擷取資料列。排序是根據聯接類型遍歷所有資料列,並為所有符合
WHERE子句的資料列儲存排序鍵和指向該資料列的指標來完成。然後對索引鍵進行排序,並以排序順序擷取資料列。請參閱 第 10.2.1.16 節,「ORDER BY 最佳化」。Using index(JSON 屬性:using_index)僅使用索引樹中的資訊,無需進行額外的搜尋以讀取實際資料列,即可從資料表擷取欄位資訊。當查詢僅使用單一索引的一部分的欄位時,可以使用此策略。
對於具有使用者定義叢集索引的
InnoDB資料表,即使Extra欄位中沒有Using index,也可以使用該索引。如果type是index且key是PRIMARY,則屬於這種情況。關於使用的任何涵蓋索引的資訊會顯示在
EXPLAIN FORMAT=TRADITIONAL和EXPLAIN FORMAT=JSON中。它也會顯示在EXPLAIN FORMAT=TREE中。Using index condition(JSON 屬性:using_index_condition)透過存取索引元組並首先測試它們以確定是否讀取完整資料表列來讀取資料表。透過這種方式,使用索引資訊來延遲(「向下推」)讀取完整資料表列,除非必要。請參閱 第 10.2.1.6 節,「索引條件下推最佳化」。
Using index for group-by(JSON 屬性:using_index_for_group_by)與
Using index資料表存取方法類似,Using index for group-by表示 MySQL 找到一個可用於擷取GROUP BY或DISTINCT查詢的所有欄位的索引,而無需對實際資料表進行任何額外的磁碟存取。此外,索引以最有效的方式使用,因此對於每個群組,僅讀取幾個索引項目。如需詳細資訊,請參閱 第 10.2.1.17 節,「GROUP BY 最佳化」。Using index for skip scan(JSON 屬性:using_index_for_skip_scan)表示使用跳躍掃描存取方法。請參閱跳躍掃描範圍存取方法。
Using join buffer (Block Nested Loop)、Using join buffer (Batched Key Access)、Using join buffer (hash join)(JSON 屬性:using_join_buffer)先前聯接的資料表會分批讀取到聯接緩衝區中,然後它們的資料列會從緩衝區中用來執行與目前資料表的聯接。
(Block Nested Loop)表示使用區塊巢狀迴圈演算法,(Batched Key Access)表示使用批次索引鍵存取演算法,而(hash join)表示使用雜湊聯接。也就是說,會緩衝EXPLAIN輸出中前一行資料表的索引鍵,並從Using join buffer出現的行所代表的資料表中分批擷取相符的資料列。在 JSON 格式的輸出中,
using_join_buffer的值始終是Block Nested Loop、Batched Key Access或hash join其中之一。如需有關雜湊聯接的詳細資訊,請參閱 第 10.2.1.4 節,「雜湊聯接最佳化」。
有關批次索引鍵存取演算法的資訊,請參閱 批次索引鍵存取聯接。
Using MRR(JSON 屬性:message)使用多範圍讀取最佳化策略讀取資料表。請參閱 第 10.2.1.11 節,「多範圍讀取最佳化」。
Using sort_union(...)、Using union(...)、Using intersect(...)(JSON 屬性:message)這些表示顯示如何合併
index_merge聯接類型索引掃描的特定演算法。請參閱 第 10.2.1.3 節,「索引合併最佳化」。Using temporary(JSON 屬性:using_temporary_table)為了解析查詢,MySQL 需要建立一個暫存資料表來保存結果。如果查詢包含
GROUP BY和ORDER BY子句,且列出的欄位不同,則通常會發生這種情況。Using where(JSON 屬性:attached_condition)使用
WHERE子句來限制要比對下一個資料表或傳送給用戶端的資料列。除非您明確打算擷取或檢查資料表中的所有資料列,否則如果Extra值不是Using where,且資料表聯接類型是ALL或index,則您的查詢可能存在問題。Using where在 JSON 格式的輸出中沒有直接對應項;attached_condition屬性包含使用的任何WHERE條件。Using where with pushed condition(JSON 屬性:message)此項目僅適用於
NDB資料表。這表示 NDB Cluster 正在使用條件下推最佳化來提高非索引欄位與常數之間直接比較的效率。在這種情況下,條件會「下推」到叢集的資料節點,並在所有資料節點上同時評估。這消除了透過網路傳送不符資料列的需要,並且與可以使用但不使用條件下推的情況相比,可以將此類查詢的速度提高 5 到 10 倍。如需詳細資訊,請參閱 第 10.2.1.5 節,「引擎條件下推最佳化」。Zero limit(JSON 屬性:message)查詢具有
LIMIT 0子句,無法選取任何資料列。
您可以藉由採用 EXPLAIN 輸出中 rows 欄位的值的乘積來判斷聯接的優劣。這應該會大略告訴您 MySQL 必須檢查多少資料列才能執行查詢。如果您使用 max_join_size 系統變數來限制查詢,則此資料列乘積也用於判斷要執行哪些多資料表 SELECT 陳述式,以及中止哪些陳述式。請參閱 第 7.1.1 節,「設定伺服器」。
下列範例顯示如何根據 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;在此範例中,請進行以下假設
正在比較的欄位已宣告如下。
資料表 欄位 資料類型 ttActualPCCHAR(10)ttAssignedPCCHAR(10)ttClientIDCHAR(10)etEMPLOYIDCHAR(15)doCUSTNMBRCHAR(15)這些表格有下列索引。
資料表 索引 ttActualPCttAssignedPCttClientIDetEMPLOYID(主鍵)doCUSTNMBR(主鍵)tt.ActualPC的值並非均勻分佈。
最初,在執行任何最佳化之前,EXPLAIN 語句會產生下列資訊
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 (index map: 0x23)由於每個表格的 type 都是 ALL,此輸出表示 MySQL 正在產生所有表格的笛卡爾積;也就是說,每一行的組合。這需要相當長的時間,因為必須檢查每個表格中的列數乘積。就目前的情況而言,此乘積為 74 × 2135 × 74 × 3872 = 45,268,558,720 行。如果表格更大,您可以想像需要多長時間。
這裡的一個問題是,如果欄位宣告為相同的類型和大小,MySQL 可以更有效率地使用欄位的索引。在此情況下,如果 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 (index map: 0x1)
et_1 ALL PRIMARY NULL NULL NULL 74
Range checked for each record (index map: 0x1)
et eq_ref PRIMARY PRIMARY 15 tt.ActualPC 1這並非完美,但好得多:rows 值的乘積減少了 74 倍。這個版本在幾秒鐘內執行完畢。
可以進行第二次變更,以消除 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 聯結最佳化工具的推測。將 rows 乘積與查詢傳回的實際列數進行比較,檢查數字是否接近實際情況。如果數字差異很大,您可能會透過在 SELECT 語句中使用 STRAIGHT_JOIN,並嘗試在 FROM 子句中以不同的順序列出表格,來獲得更好的效能。(但是,STRAIGHT_JOIN 可能會因為它停用半聯結轉換而導致無法使用索引。請參閱 使用半聯結轉換最佳化 IN 和 EXISTS 子查詢述詞。)
在某些情況下,當 EXPLAIN SELECT 與子查詢一起使用時,可以執行修改資料的語句;如需更多資訊,請參閱 第 15.2.15.8 節「衍生表格」。