文件首頁
MySQL 9.0 參考手冊
相關文件 下載本手冊
PDF (美式信紙) - 40.0Mb
PDF (A4) - 40.1Mb
Man Pages (TGZ) - 258.2Kb
Man Pages (Zip) - 365.3Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


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

10.8.2 EXPLAIN 輸出格式

EXPLAIN 陳述式提供有關 MySQL 如何執行陳述式的資訊。EXPLAIN 可與 SELECTDELETEINSERTREPLACEUPDATE 陳述式搭配使用。

EXPLAINSELECT 陳述式中使用的每個資料表傳回一列資訊。它會在輸出中依 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 輸出列的資料表
分割區 分割區 符合的分割區
類型 存取類型 聯結類型
可能的鍵值 可能的鍵值 可選擇的索引
鍵值 鍵值 實際選擇的索引
鍵值長度 鍵值長度 所選鍵值的長度
參照 參照 與索引比較的欄位
列數 列數 預計檢查的列數
過濾 過濾 資料表條件所過濾的列百分比
額外資訊 額外資訊

注意

JSON 屬性若為 NULL,則不會顯示在 JSON 格式的 EXPLAIN 輸出中。

  • id (JSON 名稱:select_id)

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

  • select_type (JSON 名稱:無)

    SELECT 的類型,可以是下表中顯示的任何一種。除非類型為 SIMPLEPRIMARY,否則 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 子查詢類型的等效屬性都可用(例如 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 欄位大 1。

  • 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 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(tbl_name) (JSON 屬性: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)

    MySQL 已讀取所有 const(和 system)表格,並注意到 WHERE 子句永遠為 false。

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

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

  • 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)

    對於 DELETEUPDATE,最佳化器在分割區修剪後發現沒有任何內容需要刪除或更新。其含義與 SELECT 陳述式的 Impossible WHERE 相似。

  • No tables used (JSON 屬性: message)

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

    對於 INSERTREPLACE 陳述式,當沒有 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 WARNINGSMessage 欄位,或在 EXPLAIN FORMAT=TREE 的輸出中,查看是否對給定查詢執行了反聯結轉換。

    注意

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

  • Plan is not ready yet (JSON 屬性: 無)

    當最佳化器尚未完成在具名連線中執行之陳述式的執行計畫建立時,此值會與 EXPLAIN FOR CONNECTION 一起發生。如果執行計畫輸出包含多行,則任何或所有行都可能具有此 Extra 值,具體取決於最佳化器在判斷完整執行計畫時的進度。

  • Range checked for each record (index map: N) (JSON 屬性: message)

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

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

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

  • Scanned N databases (JSON 屬性: message)

    這表示伺服器在處理 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)。也就是說,對於每個資料行 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 子句永遠為 true 且沒有 GROUP BY 子句,則此 Extra 值可能會出現在 COUNT(*) 查詢中。(這是隱含分組查詢的一個實例,其中儲存引擎會影響是否可以讀取確定數量的列。)

  • 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)

由於每個資料表的 typeALL,此輸出表示 MySQL 正在產生所有資料表的笛卡爾積;也就是說,每個資料列的組合。這需要相當長的時間,因為必須檢查每個資料表中的資料列數量的乘積。就目前情況而言,這個乘積是 74 × 2135 × 74 × 3872 = 45,268,558,720 個資料列。如果資料表更大,您只能想像需要多長時間。

這裡的一個問題是,如果欄位宣告為相同的類型和大小,MySQL 可以更有效率地使用欄位上的索引。在這個情況下,如果宣告為相同的大小,VARCHARCHAR 會被視為相同。tt.ActualPC 宣告為 CHAR(10),而 et.EMPLOYID 宣告為 CHAR(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,「衍生資料表」