Mysql (EXPLAIN)執行計劃之快速定位索引是否生效

添加小編微信帶您進入Java交流社區
小編微信:372787553 備註進羣

Extra 輔助信息說明

在這裏插入圖片描述
ExtraEXPLAIN輸出包含MySQL解決查詢的額外信息。以下列表說明了可以在此列中顯示的值。每個項目還針對JSON格式的輸出指示哪個屬性顯示Extra值。對於其中一些,有一個特定的屬性。其他顯示爲message 屬性的文本。

如果你想使你的查詢儘可能快,看出來Extra的列值Using filesortUsing temporary,或在JSON格式的EXPLAIN輸出,用於 using_filesortusing_temporary_table性能等於 true

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

    該表是*table*可以向下推到NDB內核的聯接中的子級引用 。啓用下推聯接時,僅適用於NDB羣集。

  • 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策略。 *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

    對於DELETEUPDATE,優化器在分區修剪後找不到要刪除或更新的內容。它的含義類似於Impossible WHERE for SELECT語句。

  • No tables used(JSON屬性: message

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

    對於INSERTREPLACE語句, EXPLAIN在沒有任何SELECT部分時顯示此值。例如,出現的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並查找行 。如果MySQL在中找到匹配的行 ,它將知道它 永遠不會是 ,並且不會掃描具有相同值的其餘行。換句話說,對於in中的每一行,MySQL 實際上只需進行一次查找,無論in中實際匹配多少行。 t2``t1.id``t2``t2.id``NULL``t2``id``t1``t2``t2

  • Plan isn't ready yet (JSON屬性:無)

    EXPLAIN FOR CONNECTION當優化器尚未完成爲在命名連接中執行的語句創建執行計劃時, 就會出現此值。如果執行計劃輸出包含多行,則Extra取決於優化程序確定完整執行計劃的進度,其中任何一行或所有行都可以具有此 值。

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

    MySQL找不到很好的索引來使用,但是發現一些索引可以在已知先前表中的列值之後使用。對於上表中的每個行組合,MySQL檢查是否可以使用rangeindex_merge訪問方法來檢索行。這不是很快,但是比完全沒有索引的連接要快。適用標準如“範圍優化”和 “索引合併優化”中所述,除了上表的所有列值都是已知的並且被視爲常量。

    索引從1開始編號,其順序SHOW INDEX與表中顯示的順序相同。索引圖值 *N*是指示哪些索引爲候選的位掩碼值。例如,值0x19(二進制11001)表示將考慮索引1、4和5。

  • Scanned *N* databases(JSON屬性: message

    這表示在處理INFORMATION_SCHEMA表查詢時服務器執行了多少目錄掃描 ,如“優化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),對於缺少該子句或始終爲true且沒有 子句的查詢,Extra 可能會出現此值。(這是一個隱式分組查詢的實例,其中存儲引擎影響是否可以讀取確定數量的行。) COUNT(*)``WHERE``GROUP BY

  • Skip_open_tableOpen_frm_onlyOpen_full_table(JSON屬性: message

    這些值指示適用於INFORMATION_SCHEMA 表查詢的文件打開優化,如“優化INFORMATION_SCHEMA查詢”中所述。

    • Skip_open_table:不需要打開表文件。通過掃描數據庫目錄,該信息已在查詢中可用。
    • Open_frm_only:僅.frm需要打開表的文件。
    • Open_full_table:未優化的信息查找。的.frm.MYD.MYI文件必須被打開。
  • 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子句匹配的所有行的排序鍵和指向該行的指針來完成的。然後對鍵進行排序,並按排序順序檢索行。

  • Using index(JSON屬性: using_index

    僅使用索引樹中的信息從表中檢索列信息,而不必進行其他查找以讀取實際行。當查詢僅使用屬於單個索引的列時,可以使用此策略。

    對於InnoDB具有用戶定義的聚集索引的表,即使列中Using index不存在 該索引也可以使用Extra。如果typeis indexkeyis 就是這種情況 PRIMARY

  • Using index condition(JSON屬性: using_index_condition

    通過訪問索引元組並首先對其進行測試以確定是否讀取完整的錶行來讀取表。這樣,除非必要,否則索引信息將用於延遲(“ 下推 ”)讀取整個錶行。

  • Using index for group-by(JSON屬性:using_index_for_group_by

    Using index表訪問方法類似,Using index for group-by 表示MySQL找到了一個索引,該索引可用於檢索a GROUP BYDISTINCT查詢的所有列,而無需對實際表進行任何額外的磁盤訪問。此外,以最有效的方式使用索引,因此對於每個組,僅讀取少數索引條目。

  • Using join buffer (Block Nested Loop)Using join buffer (Batched Key Access) (JSON屬性:using_join_buffer

    來自較早聯接的表被部分讀取到聯接緩衝區中,然後從緩衝區中使用它們的行來執行與當前表的聯接。 (Block Nested Loop)表示使用塊嵌套循環算法,並(Batched Key Access)表示使用批處理密鑰訪問算法。也就是說,將EXPLAIN緩衝輸出前行中的表中的鍵 ,並從出現行所在的表中批量提取匹配的行 Using join buffer

    在JSON格式的輸出中,的值 using_join_buffer始終爲Block Nested Loop或之一 Batched Key Access

  • Using MRR(JSON屬性: message

    使用多範圍讀取優化策略讀取表。

  • Using sort_union(...)Using union(...)Using intersect(...)(JSON屬性: message

    這些指示了特定算法,該算法顯示瞭如何針對index_merge聯接類型合併索引掃描 。

  • Using temporary(JSON屬性: using_temporary_table

    爲了解決該查詢,MySQL需要創建一個臨時表來保存結果。如果查詢包含GROUP BYORDER 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倍。

  • Zero limit(JSON屬性: message

    該查詢有一個LIMIT 0子句,不能選擇任何行。

解釋輸出解釋

通過獲取輸出rows 列中值的乘積,可以很好地表明聯接的良好程度EXPLAIN。這應該大致告訴您MySQL必須檢查多少行才能執行查詢。如果使用max_join_size系統變量限制查詢,則 此行乘積還用於確定SELECT 執行哪些多表語句以及中止哪個多表語句。

以下示例顯示瞭如何根據提供的信息逐步優化多表聯接 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.EMPLOYIDCHAR(15),因此長度不匹配。

要解決此列長度之間的差異,請使用 從10個字符ALTER TABLE延長 ActualPC到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

rows從輸出列 EXPLAIN是一個受過教育的猜測從MySQL聯接優化。通過將rows乘積與查詢返回的實際行數進行比較,檢查數字是否接近真實 值。如果數字完全不同,則可以通過STRAIGHT_JOINSELECT語句中使用並嘗試在FROM子句中以不同順序列出表來 獲得更好的性能 。(但是,STRAIGHT_JOIN由於它禁用了半聯接轉換, 可能會阻止使用索引。

在某些情況下,可能會執行EXPLAIN SELECT與子查詢一起使用時會修改數據的語句。

本文的分享暫時就到這裏,希望對您有所幫助
關注 Java有貨領取更多資料

聯繫小編。微信:372787553,帶您進羣互相學習
左側小編微信,右側獲取免費資料
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章