mysql索引最左匹配原則與explain語句各列的意義

測試所用表結構

CREATE TABLE `t_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `sex` char(1) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_age_name` (`age`,`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

explain結果中的每一列意義

id: select識別符。這是select的查詢序列號。

select_type: select 類型

  • SIMPLE:查詢不包括子查詢和UNION
  • SUBQUERY:包含在select 列表中的子查詢,不在from 子句中,標記爲SUBQUERY。
  • DERIVED:標記包含在from 中的子查詢,mysql會遞歸查詢並將結果放到一個臨時表中。服務器內部稱其“派生表”
  • UNION:聯合查詢,在UNION 中第二個和隨後的查詢被標記爲UNION。第一個select以部分外查詢來執行,所以被顯示爲primary。如果UNION被from子句中的子查詢包含,那麼它的第一個select
    會被標記爲derived
  • UNION RESULT: 用來從union的匿名臨時表檢索結果的select被標記爲UNION RESULT。
    除了這些, SUBQUERY和UNION還可以被標記爲DEPENDENT何UNCACHEABLE。
  • DEPENDENT意味着select依賴於外層查詢中發現的數據。UNCACHEABLE意味着select中的某些特性阻止結果被緩存於一個Item_cache中。(Item_cache未被文檔記載,它與查詢緩存不是一回事,儘管它可以被一些相同類型的構件否定,例如RAND()函數。)

table: 對應行正在訪問的表。如果是派生表和聯合表會複雜很多。

  • 當在from子句中有子查詢時,table列是<derivedN>的形式,其中N是子查詢的id。這總是“向前引用”,即N指向explain輸出中後面的一行。
  • 當有union時,union result的table列包含一個參與union的id列表。這總是“向後引用”,因爲union result出現在union中所有參與行之後

type: 表示表的連接類型,更準確的說法是訪問類型——換言之就是mysql決定如何查找表中的行。依次從最差到最優:

  • ALL:這就是所謂的全表掃描
  • index:這個跟全表掃描一樣,只是mysql掃描表時按索引次序而不是行,它的主要優點是避免了排序;最大缺點是要承擔按索引次序讀取整個表的開銷。這通常意味着若是按隨機次序訪問行,開銷將非常大。如果在extra列中看到“Using index”,說明mysql正在使用索引覆蓋,它只掃描索引的數據,而不是按索引次序的每一行(如select id from table,只用到索引的數據,不用索引數據文件),它比按索引次序全表掃描的開銷要少很多。
  • range:範圍掃描就是一個有限制的索引掃描,它開始與索引裏的某一點,返回匹配這個值域的行,這比全索引掃描要好一點,因爲它用不着遍歷全部索引,顯而易見的掃描是帶有between或在where子句裏帶有>的查詢。 當mysql使用索引去查找一系列值時,例如in()和or列表,也會顯示爲範圍掃描,然而,這兩者其實是相當不同的訪問類型,在性能上有重要的差異(結論:or的效率爲O(n),而in的效率爲O(logn),如果in和or所在列有索引或者主鍵的話,or和in沒啥差別,執行計劃和執行時間都幾乎一樣。如果in和or所在列沒有索引的話in效率高。)。此類掃描的開銷跟索引類型的相當。
  • ref:這是一種索引訪問(有時也叫做索引查找),它返回所有匹配某個單個值的行,然而,它可能會找到多個符合條件的行。因此,它是查找和掃描的混合體,此類索引訪問只有當使用非唯一性索引或者唯一性索引的非唯一性前綴時纔會發生。把它叫做ref是因爲索引要跟某個參考值相比較。這個參考值或者是一個常數,或者是來自多表查詢前一個表裏的結果值。
    ref_or_null是ref之上的一個變體,它意味着mysql必須在初次查找的結果裏進行第二次查找以找出null條目。
  • eq_ref:使用這種索引查找,mysql知道最多隻返回一條符合條件的記錄,這種訪問方法可以在mysql使用主鍵或者唯一性索引查找時看到,它會將它們與某個參考值做比較。mysql對於這類訪問類型的優化做得非常好,因爲它知道無需估計匹配行的範圍或在找到匹配行後再繼續查找。
  • const,system:當mysql能對查詢的某部分進行優化並將其轉換成一個常量時,他就會使用這些訪問類型,舉例來說,如果你通過將某一行的主鍵放入where子句裏的方式來選取此行的主鍵,mysql就能把這個查詢轉換爲一個常量,然後就可以高效地將表從聯接執行中移除。
  • null:這種訪問方式意味着mysql能在優化階段分解查詢語句,在執行階段甚至用不着再訪問表或者索引。例如,從一個索引列裏選取最小值可以通過單獨查找索引來完成,不需要在執行時訪問表。
    當表中僅有一行是type的值爲system是最佳的連接類型;
    當select操作中使用索引進行表連接時type的值爲ref;
    當select的表連接沒有使用索引時,經常會看到type的值爲ALL,表示對該表進行了全表掃描,這時需要考慮通過創建索引來提高表連接的效率。

possible_keys: 表示查詢時,可以使用的索引列,這是基於查詢訪問的列和使用的比較操作符來判斷的。

key: mysql決定採用的索引來優化對該表的訪問。如果該索引沒有出現在possible_keys列中,那麼mysql選用它是處於另外的原因,如它可能選擇了一個覆蓋索引,哪怕沒有where子句。possible_keys揭示了哪一個索引能有助於高效地進行查找,而key顯示的是優化採用哪一個索引可以最小化查詢成本。

key_len: 列顯示了在索引字段中可能的最大長度,而不是表中數據使用的實際字節數,換言之,key_len通過查找表的定義而被計算出,而不是表中的數據。

**ref:**顯示了之前的表在key列記錄的索引中查找值所用的列或常量

rows: 這一列是mysql估計爲了找到所需的行而要讀取的行數。這個數字是內嵌循環關聯計劃裏的 循環數目,也就是說它不是mysql認爲它最終要從表裏讀取出來的行數,而是mysql爲了找到符合查詢的每一點上標準的那些行而必須讀取的行的平均數。

Extra: 包含的是不適合在其他列顯示的額外信息。常見的最重要的值如下:

  • Using index 此值表示mysql將使用覆蓋索引,以避免訪問表。不要把覆蓋索引和index訪問類型弄混了。
  • Using where 這意味着mysql服務器將在存儲引擎檢索行後再進行過濾,許多where條件裏涉及索引中的列,當(並且如果)它讀取索引時,就能被存儲引擎檢驗,因此不是所有帶where子句的查詢都會顯示“Using where”。有時“Using where”的出現就是一個暗示:查詢可受益於不同的索引。
  • Using temporary 這意味着mysql在對查詢結果排序時會使用一個臨時表。
  • Using filesort 這意味着mysql會對結果使用一個外部索引排序,而不是按索引次序從表裏讀取行。mysql有兩種文件排序算法,這兩種排序方式都可以在內存或者磁盤上完成,explain不會告訴你mysql將使用哪一種文件排序,也不會告訴你排序會在內存裏還是磁盤上完成。
  • Range checked for each record(index map: N)
    這個意味着沒有好用的索引,新的索引將在聯接的每一行上重新估算,N是顯示在possible_keys列中索引的位圖,並且是冗餘的。

最左匹配原則

最左優先,以最左邊的爲起點任何連續的索引都能匹配上。同時遇到範圍查詢(>、<、between、like)就會停止匹配。

此時,已經建立索引index_age_name (age,name)

測試全值匹配查詢

EXPLAIN SELECT * FROM T_USER WHERE NAME = '1' AND AGE=1;
EXPLAIN SELECT * FROM T_USER WHERE AGE=1 AND NAME = '1';

執行結果如下:
在這裏插入圖片描述

在這裏插入圖片描述
可以看到這裏都用到了索引,where子句幾個搜索條件順序調換不影響查詢結果,有的文章中寫到where條件順序與索引順序不一致得到情況下會用不到索引,這裏測試結果並非如此,是因爲Mysql中有查詢優化器,會自動優化查詢順序 。mysql查詢優化器會判斷糾正這條sql語句該以什麼樣的順序執行效率最高,最後才生成真正的執行計劃。所以,當然是我們能儘量的利用到索引時的查詢順序效率最高咯,所以mysql查詢優化器會最終以這種順序進行查詢執行。

匹配左邊的列

EXPLAIN SELECT * FROM T_USER WHERE AGE = 1;
EXPLAIN SELECT * FROM T_USER WHERE NAME = '1';

執行結果如下:

在這裏插入圖片描述
在這裏插入圖片描述

可以看到,當查詢條件只有索引的部分時,第一個sql用到了索引,第二個沒有,這裏用到的規則是從索引的左邊開始(此處爲age–>name)如果連續匹配,則會使用索引,若沒從最左邊開始,則不會。比如索引index(a,b,c),如果where b=x and c=ywhere b=x則不會用到索引 ,若從左邊開始,但不連續,如where a=x and c=y ,這種情況就只用到了a列的索引,b列和c列都沒有用到 。

匹配列前綴

這裏新建索引index_name(name)
執行以下語句

EXPLAIN SELECT * FROM T_USER WHERE  NAME LIKE '1%';
EXPLAIN SELECT * FROM T_USER WHERE  NAME LIKE '%1%';
EXPLAIN SELECT * FROM T_USER WHERE  NAME LIKE '%1';

執行結果:

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

得出結論:如果是字符類型,那麼前綴匹配用的是索引,後綴和中綴只能全表掃描了。

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