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 時,輸出中顯示的對等屬性名稱。
JSON 屬性若為 NULL,則不會顯示在 JSON 格式的 EXPLAIN 輸出中。
SELECT識別碼。這是查詢中SELECT的循序編號。如果該列參考其他列的聯集結果,則該值可能為NULL。在這種情況下,table欄位會顯示類似<union的值,表示該列參考M,N>id值為M和N的列的聯集。SELECT的類型,可以是下表中顯示的任何一種。除非類型為SIMPLE或PRIMARY,否則 JSON 格式的EXPLAIN會將SELECT類型公開為query_block的屬性。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欄位大 1。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 Cluster,當啟用向下推送聯結時。如需更多資訊和範例,請參閱ndb_join_pushdown伺服器系統變數的說明。const row not found(JSON 屬性:const_row_not_found)對於像
SELECT ... FROM這樣的查詢,表格是空的。tbl_nameDeleting all rows(JSON 屬性:message)對於
DELETE,某些儲存引擎(例如MyISAM)支援一種處理常式方法,以簡單快速的方式移除所有表格列。如果引擎使用此最佳化,就會顯示此Extra值。Distinct(JSON 屬性:distinct)MySQL 正在尋找不重複的值,因此在找到第一個相符的列之後,它會停止搜尋目前列組合的更多列。
FirstMatch((JSON 屬性:tbl_name)first_match)半聯結 FirstMatch 聯結捷徑策略會用於
tbl_name。Full scan on NULL key(JSON 屬性:message)當最佳化工具無法使用索引查詢存取方法時,這會作為子查詢最佳化的備用策略發生。
Impossible HAVING(JSON 屬性:message)HAVING子句永遠為 false,無法選取任何列。Impossible WHERE(JSON 屬性:message)WHERE子句永遠為 false,無法選取任何列。Impossible WHERE noticed after reading const tables(JSON 屬性:message)LooseScan((JSON 屬性:m..n)message)使用半聯結 LooseScan 策略。
m和n是索引鍵部分編號。No matching min/max row(JSON 屬性:message)沒有列滿足諸如
SELECT MIN(...) FROM ... WHERE的查詢條件。conditionno matching row in const table(JSON 屬性:message)對於具有聯結的查詢,有一個空表格或一個表格中沒有列滿足唯一索引條件。
No matching rows after partition pruning(JSON 屬性:message)對於
DELETE或UPDATE,最佳化器在分割區修剪後發現沒有任何內容需要刪除或更新。其含義與SELECT陳述式的Impossible WHERE相似。No tables used(JSON 屬性:message)查詢沒有
FROM子句,或具有FROM DUAL子句。對於
INSERT或REPLACE陳述式,當沒有SELECT部分時,EXPLAIN會顯示此值。例如,它會出現在EXPLAIN INSERT INTO t VALUES(10)中,因為這等效於EXPLAIN INSERT INTO t SELECT 10 FROM DUAL。Not exists(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的列的列。Plan is not ready yet(JSON 屬性: 無)當最佳化器尚未完成在具名連線中執行之陳述式的執行計畫建立時,此值會與
EXPLAIN FOR CONNECTION一起發生。如果執行計畫輸出包含多行,則任何或所有行都可能具有此Extra值,具體取決於最佳化器在判斷完整執行計畫時的進度。Range checked for each record (index map:(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。Recursive(JSON 屬性:recursive)這表示列適用於遞迴通用表格運算式的遞迴
SELECT部分。請參閱 第 15.2.20 節「WITH (通用表格運算式)」。Rematerialize(JSON 屬性:rematerialize)當讀取
T的新列時,會觸發重新實體化,則會在表格T的EXPLAIN列中顯示Rematerialize (X,...),其中X是任何側向衍生表格。例如SELECT ... FROM t, LATERAL (derived table that refers to t) AS dt ...每次頂層查詢處理
t的新列時,都會重新實體化衍生表格的內容,使其保持最新狀態。Scanned(JSON 屬性:Ndatabasesmessage)這表示伺服器在處理
INFORMATION_SCHEMA表格的查詢時執行了多少目錄掃描,如 第 10.2.3 節「最佳化 INFORMATION_SCHEMA 查詢」中所述。N的值可以是 0、1 或all。Select tables optimized away(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子句永遠為 true 且沒有GROUP BY子句,則此Extra值可能會出現在COUNT(*)查詢中。(這是隱含分組查詢的一個實例,其中儲存引擎會影響是否可以讀取確定數量的列。)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,「衍生資料表」。