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
無 最外層的 SELECT
UNION
無 UNION
中第二個或後續的SELECT
陳述式DEPENDENT UNION
dependent
(true
)UNION
中第二個或後續的SELECT
陳述式,依賴於外部查詢UNION RESULT
union_result
UNION
的結果。SUBQUERY
無 子查詢中的第一個 SELECT
DEPENDENT SUBQUERY
dependent
(true
)子查詢中的第一個 SELECT
,依賴於外部查詢DERIVED
無 衍生資料表 DEPENDENT DERIVED
dependent
(true
)依賴於另一個資料表的衍生資料表 MATERIALIZED
materialized_from_subquery
具體化子查詢 UNCACHEABLE SUBQUERY
cacheable
(false
)結果無法快取且必須針對外部查詢的每一列重新評估的子查詢 UNCACHEABLE UNION
cacheable
(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_name
key
欄會指示 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_name
Deleting 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
的查詢條件。condition
no 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_a
JOINtable_b
ONcondition
table_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 屬性:N
databasesmessage
)這表示伺服器在處理
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_name
UNIQUE
索引或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;
在此範例中,請做以下假設
正在比較的欄位已宣告如下。
資料表 欄 資料類型 tt
ActualPC
CHAR(10)
tt
AssignedPC
CHAR(10)
tt
ClientID
CHAR(10)
et
EMPLOYID
CHAR(15)
do
CUSTNMBR
CHAR(15)
資料表具有以下索引。
資料表 索引 tt
ActualPC
tt
AssignedPC
tt
ClientID
et
EMPLOYID
(主索引鍵)do
CUSTNMBR
(主索引鍵)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,「衍生資料表」。