文件首頁
MySQL 8.4 參考手冊
相關文件 下載本手冊
PDF (US Ltr) - 39.9Mb
PDF (A4) - 40.0Mb
Man Pages (TGZ) - 258.5Kb
Man Pages (Zip) - 365.5Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 8.4 參考手冊  /  ...  /  EXPLAIN 輸出格式

10.8.2 EXPLAIN 輸出格式

EXPLAIN 陳述式提供關於 MySQL 如何執行陳述式的資訊。EXPLAIN 適用於 SELECTDELETEINSERTREPLACEUPDATE 陳述式。

EXPLAIN 會針對 SELECT 陳述式中使用的每個資料表傳回一列資訊。它會在輸出中依 MySQL 在處理陳述式時讀取資料表的順序來列出資料表。這表示 MySQL 會從第一個資料表讀取一列,然後在第二個資料表中找到符合的資料列,然後在第三個資料表中找到,依此類推。當所有資料表都處理完畢後,MySQL 會輸出選取的資料行,並回溯資料表清單,直到找到一個有更多符合資料列的資料表。會從這個資料表讀取下一列,並繼續處理下一個資料表。

注意

MySQL Workbench 具有 Visual Explain 功能,可提供 EXPLAIN 輸出的視覺化呈現。請參閱 教學:使用 Explain 來改善查詢效能

EXPLAIN 輸出資料行

本節說明 EXPLAIN 產生的輸出資料行。後面的章節提供關於 typeExtra 資料行的其他資訊。

來自 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 屬性。

  • id (JSON 名稱:select_id)

    SELECT 識別碼。這是查詢中 SELECT 的循序編號。如果該列是指其他列的聯集結果,則該值可能為 NULL。在此情況下,table 欄會顯示類似 <unionM,N> 的值,以表示該列是指 id 值為 MN 的列的聯集。

  • select_type (JSON 名稱:無)

    SELECT 的類型,可以是下表中顯示的任何一種。JSON 格式的 EXPLAIN 會將 SELECT 類型公開為 query_block 的屬性,除非它是 SIMPLEPRIMARY。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 子查詢類型對應的屬性都可用 (例如 MATERIALIZEDmaterialized_from_subquery),並會在適當時顯示。沒有 SIMPLEPRIMARY 的 JSON 對應項目。

    SELECT 陳述式的 select_type 值會顯示受影響資料表的陳述式類型。例如,對於 DELETE 陳述式,select_typeDELETE

  • table (JSON 名稱:table_name)

    輸出列所指的資料表名稱。這也可以是下列其中一個值

    • <unionM,N>:該列是指 id 值為 MN 的列的聯集。

    • <derivedN>:該列是指 id 值為 N 的列的衍生資料表結果。例如,衍生資料表可能是來自 FROM 子句中的子查詢。

    • <subqueryN>:該列是指 id 值為 N 的列的具體化子查詢結果。請參閱第 10.2.2.2 節「使用具體化最佳化子查詢」

  • partitions (JSON 名稱:partitions)

    查詢會從中比對記錄的分區。對於非分割資料表,值為 NULL。請參閱第 26.3.5 節「取得有關分區的資訊」

  • type (JSON 名稱:access_type)

    聯結類型。如需不同類型的描述,請參閱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 (JSON 名稱:key)

    key 欄位指出 MySQL 實際決定使用的索引鍵 (索引)。如果 MySQL 決定使用 possible_keys 索引之一來尋找列,則該索引會列為索引鍵值。

    key 可能會命名不在 possible_keys 值中的索引。如果沒有任何 possible_keys 索引適用於尋找列,但查詢選取的所有欄都是其他一些索引的欄,就可能會發生這種情況。也就是說,已命名的索引涵蓋選取的欄,因此雖然它不被用於決定要擷取的列,但索引掃描比資料列掃描更有效率。

    對於 InnoDB,即使查詢也選取主索引鍵,次要索引也可能涵蓋選取的欄,因為 InnoDB 會將主索引鍵值與每個次要索引一起儲存。如果 keyNULL,則表示 MySQL 找不到任何可更有效率地執行查詢的索引。

    若要強制 MySQL 使用或忽略 possible_keys 欄中列出的索引,請在查詢中使用 FORCE INDEXUSE INDEXIGNORE INDEX。請參閱第 10.9.4 節「索引提示」

    對於 MyISAM 資料表,執行 ANALYZE TABLE 有助於最佳化工具選擇更好的索引。對於 MyISAM 資料表,myisamchk --analyze 的作用相同。請參閱第 15.7.3.1 節「ANALYZE TABLE 陳述式」第 9.6 節「MyISAM 資料表維護和損毀復原」

  • key_len (JSON 名稱:key_length)

    key_len 欄位指出 MySQL 決定使用的索引鍵長度。key_len 的值可讓您判斷 MySQL 實際使用了多部分索引鍵的多少部分。如果 key 欄表示 NULL,則 key_len 欄也會表示 NULL

    由於索引鍵儲存格式,可為 NULL 的欄的索引鍵長度比 NOT NULL 欄的索引鍵長度多一。

  • ref (JSON 名稱:ref)

    ref 欄顯示與 key 欄中命名的索引比較以從資料表中選取列的欄或常數。

    如果值為 func,則使用的值是某些函式的結果。若要查看哪個函式,請在 EXPLAIN 之後使用 SHOW WARNINGS,以查看擴充的 EXPLAIN 輸出。函式實際上可能是運算子,例如算術運算子。

  • rows (JSON 名稱:rows)

    rows 欄位指出 MySQL 認為它必須檢查以執行查詢的列數。

    對於 InnoDB 資料表,此數字為估計值,可能並不總是精確。

  • filtered (JSON 名稱:filtered)

    filtered 欄位表示表格條件篩選的預估資料列百分比。最大值為 100,表示未發生資料列篩選。從 100 遞減的值表示篩選量增加。rows 顯示預估的檢查資料列數,而 rows × filtered 顯示與後續表格聯結的資料列數。例如,如果 rows 為 1000 且 filtered 為 50.00 (50%),則要與後續表格聯結的資料列數為 1000 × 50% = 500。

  • Extra (JSON 名稱:無)

    此欄位包含有關 MySQL 如何解析查詢的其他資訊。如需不同值的說明,請參閱EXPLAIN 額外資訊

    沒有與 Extra 欄位對應的單一 JSON 屬性;但是,此欄位中可能出現的值會以 JSON 屬性或 message 屬性的文字形式顯示。

EXPLAIN 聯結類型

EXPLAIN 輸出的 type 欄位描述表格如何聯結。在 JSON 格式的輸出中,這些會以 access_type 屬性的值找到。以下清單描述了聯結類型,從最佳類型到最差類型排序。

  • system

    該表格只有一個資料列(= 系統表格)。這是 const 聯結類型的特殊情況。

  • const

    表格最多有一個符合的資料列,在查詢開始時讀取。由於只有一個資料列,此資料列中欄位的值可以被最佳化工具的其餘部分視為常數。const 表格非常快,因為它們只讀取一次。

    當您將 PRIMARY KEYUNIQUE 索引的所有部分與常數值比較時,會使用 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;
  • eq_ref

    從這個表格中讀取一個資料列,對應於先前表格的每個資料列組合。除了 systemconst 類型之外,這是最佳的聯結類型。當聯結使用索引的所有部分,且索引是 PRIMARY KEYUNIQUE 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;
  • ref

    從這個表格中讀取具有匹配索引值的所有資料列,對應於先前表格的每個資料列組合。如果聯結僅使用索引的最左邊前綴,或者如果索引不是 PRIMARY KEYUNIQUE 索引(換句話說,如果聯結無法根據索引值選取單一資料列),則會使用 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

    聯結是使用 FULLTEXT 索引執行的。

  • ref_or_null

    此聯結類型類似於 ref,但增加了一項功能,MySQL 會額外搜尋包含 NULL 值的資料列。此聯結類型最佳化最常用於解析子查詢。在以下範例中,MySQL 可以使用 ref_or_null 聯結來處理 ref_table

    SELECT * FROM ref_table
      WHERE key_column=expr OR key_column IS NULL;

    請參閱第 10.2.1.15 節,「IS NULL 最佳化」

  • index_merge

    此聯結類型表示使用索引合併最佳化。在這種情況下,輸出列中的 key 欄位包含所用索引的清單,而 key_len 包含所用索引的最長索引部分清單。如需更多資訊,請參閱第 10.2.1.3 節,「索引合併最佳化」

  • unique_subquery

    此類型會針對以下形式的某些 IN 子查詢取代 eq_ref

    value IN (SELECT primary_key FROM single_table WHERE some_expr)

    unique_subquery 只是一個索引查找函式,完全取代子查詢以提高效率。

  • index_subquery

    此聯結類型類似於 unique_subquery。它會取代 IN 子查詢,但它適用於以下形式的子查詢中的非唯一索引。

    value IN (SELECT key_column FROM single_table WHERE some_expr)
  • range

    只會使用索引選取在指定範圍內的資料列。輸出列中的 key 欄位表示使用哪個索引。key_len 包含使用的最長索引部分。對於此類型,ref 欄位為 NULL

    當使用任何 =<>>>=<<=IS NULL<=>BETWEENLIKEIN() 運算子將索引欄位與常數比較時,可以使用 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

    index 聯結類型與 ALL 相同,只是掃描索引樹。發生這種情況有兩種方式:

    • 如果索引是查詢的涵蓋索引,並且可用於滿足表格所需的所有資料,則只會掃描索引樹。在這種情況下,Extra 欄位會顯示 Using index。索引掃描通常比 ALL 快,因為索引的大小通常小於表格資料。

    • 使用從索引讀取的方式,依索引順序查找資料列來執行完整表格掃描。Uses index 不會出現在 Extra 欄位中。

    當查詢只使用單一索引的一部分的欄位時,MySQL 可以使用此聯結類型。

  • ALL

    針對先前表格的每個資料列組合執行完整表格掃描。如果表格是第一個未標示為 const 的表格,通常情況下這不是好事,而且在所有其他情況下通常 非常 糟糕。通常,您可以透過新增索引來避免 ALL,這些索引可以根據常數值或先前表格的欄位值,從表格啟用資料列擷取。

EXPLAIN 額外資訊

EXPLAIN 輸出的 Extra 欄位包含有關 MySQL 如何解析查詢的其他資訊。以下清單說明此欄位中可能出現的值。每個項目也表示 JSON 格式的輸出,其中哪個屬性會顯示 Extra 值。對於其中一些,有特定的屬性。其他則顯示為 message 屬性的文字。

如果您想要讓查詢盡可能快速,請注意 Extra 欄位的值 Using filesortUsing temporary,或者在 JSON 格式的 EXPLAIN 輸出中,注意 using_filesortusing_temporary_table 屬性等於 true

  • Backward index scan (JSON: backward_index_scan)

    最佳化工具能夠在 InnoDB 表格上使用降序索引。與 Using index 一起顯示。如需更多資訊,請參閱第 10.3.13 節,「降序索引」

  • Child of 'table' pushed join@1 (JSON: message 文字)

    此表格在可以向下推送到 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(tbl_name) (JSON 屬性:first_match)

    半聯結 FirstMatch 聯結捷徑策略用於 tbl_name

  • 在 NULL 鍵上完整掃描 (JSON 屬性:message)

    當最佳化工具無法使用索引查詢存取方法時,這會作為子查詢最佳化的回退策略發生。

  • 不可能的 HAVING (JSON 屬性:message)

    HAVING 子句始終為 false,無法選取任何列。

  • 不可能的 WHERE (JSON 屬性:message)

    WHERE 子句始終為 false,無法選取任何列。

  • 讀取 const 資料表後發現不可能的 WHERE (JSON 屬性:message)

    MySQL 已讀取所有 const (和 system)資料表,並注意到 WHERE 子句始終為 false。

  • LooseScan(m..n) (JSON 屬性:message)

    使用半聯結 LooseScan 策略。mn 是索引鍵部分編號。

  • 沒有符合的 min/max 列 (JSON 屬性:message)

    沒有列滿足諸如 SELECT MIN(...) FROM ... WHERE condition 之類查詢的條件。

  • 在 const 資料表中沒有符合的列 (JSON 屬性:message)

    對於具有聯結的查詢,存在一個空的資料表或一個沒有列滿足唯一索引條件的資料表。

  • 在分割區修剪後沒有符合的列 (JSON 屬性:message)

    對於 DELETEUPDATE,最佳化工具在分割區修剪後發現沒有要刪除或更新的內容。它的含義類似於 SELECT 陳述式的 Impossible WHERE

  • 沒有使用資料表 (JSON 屬性:message)

    查詢沒有 FROM 子句,或有 FROM DUAL 子句。

    對於 INSERTREPLACE 陳述式,當沒有 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 條件已在內部轉換為反聯結。這會移除子查詢,並將其資料表帶入最上層查詢的計畫中,從而提供更佳的成本規劃。透過合併半聯結和反聯結,最佳化工具可以在執行計畫中更自由地重新排序資料表,在某些情況下會產生更快的計畫。

    您可以透過檢查執行 EXPLAINSHOW WARNINGS 中的 Message 欄位,或在 EXPLAIN FORMAT=TREE 的輸出中,查看是否針對給定查詢執行反聯結轉換。

    注意

    反聯結是半聯結 table_a JOIN table_b ON condition 的補集。反聯結會從 table_a 中傳回所有在 table_b沒有 符合 condition 的列。

  • 計畫尚未準備好 (JSON 屬性:無)

    當最佳化工具尚未完成為具名連線中執行的陳述式建立執行計畫時,此值會與 EXPLAIN FOR CONNECTION 一起出現。如果執行計畫輸出包含多行,則其中任何或全部行都可能具有此 Extra 值,具體取決於最佳化工具在確定完整執行計畫方面的進度。

  • 為每個記錄檢查範圍 (索引圖:N) (JSON 屬性:message)

    MySQL 找不到要使用的良好索引,但發現可以使用前述資料表中的資料行值來使用某些索引。對於前述資料表中的每個列組合,MySQL 會檢查是否可以使用 rangeindex_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,...) 會顯示在資料表 TEXPLAIN 列中,其中 X 是任何橫向衍生資料表,當讀取 T 的新列時,會觸發其重新實體化。例如

    SELECT
      ...
    FROM
      t,
      LATERAL (derived table that refers to t) AS dt
    ...

    每次頂層查詢處理 t 的新列時,都會重新實體化衍生資料表的內容,以使其保持最新狀態。

  • 已掃描 N 個資料庫 (JSON 屬性: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)。也就是說,對於每個資料行 c1c2,都存在一個索引,其中該資料行是索引的第一個資料行。在這種情況下,會傳回一個列,該列是透過讀取兩個確定性的列而產生的。

    如果要讀取的列不是確定性的,則不會出現此 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_tableOpen_frm_onlyOpen_full_table (JSON 屬性:message)

    這些值表示適用於 INFORMATION_SCHEMA 資料表查詢的檔案開啟最佳化。

    • Skip_open_table:不需要開啟資料表檔案。該資訊已從資料字典中取得。

    • Open_frm_only:只需要讀取資料字典即可取得資料表資訊。

    • Open_full_table:未最佳化的資訊查詢。必須從資料字典讀取資料表資訊,並讀取資料表檔案。

  • Start temporaryEnd 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,也可以使用該索引。如果 typeindexkeyPRIMARY,則屬於這種情況。

    關於使用的任何涵蓋索引的資訊會顯示在 EXPLAIN FORMAT=TRADITIONALEXPLAIN 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 BYDISTINCT 查詢的所有欄位的索引,而無需對實際資料表進行任何額外的磁碟存取。此外,索引以最有效的方式使用,因此對於每個群組,僅讀取幾個索引項目。如需詳細資訊,請參閱 第 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 LoopBatched Key Accesshash 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 BYORDER BY 子句,且列出的欄位不同,則通常會發生這種情況。

  • Using where (JSON 屬性:attached_condition)

    使用 WHERE 子句來限制要比對下一個資料表或傳送給用戶端的資料列。除非您明確打算擷取或檢查資料表中的所有資料列,否則如果 Extra 值不是 Using where,且資料表聯接類型是 ALLindex,則您的查詢可能存在問題。

    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 輸出解讀

您可以藉由採用 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 可以更有效率地使用欄位的索引。在此情況下,如果 VARCHARCHAR 宣告為相同的大小,則會被視為相同。tt.ActualPC 宣告為 CHAR(10),而 et.EMPLOYIDCHAR(15),因此長度不符。

為了修正欄位長度之間的差異,請使用 ALTER TABLEActualPC 的長度從 10 個字元增加到 15 個字元

mysql> ALTER TABLE tt MODIFY ActualPC VARCHAR(15);

現在 tt.ActualPCet.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.EMPLOYIDtt.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 節「衍生表格」