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
無 最外層的 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
欄的索引鍵長度多一。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_a
JOINtable_b
ONcondition
table_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_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 節「衍生表格」。