Mysql索引最左匹配原則


先來看個例子:

1. 示例1:假設有如下的一張表:

  1. DROP TABLE IF EXISTS testTable;  
  2. CREATE TABLE testTable  
  3. (  
  4. ID BIGINT NOT NULL AUTO_INCREMENT COMMENT 'auto increment id',  
  5. HOST_NAME VARCHAR(64) NOT NULL COMMENT 'host name',  
  6. PORT VARCHAR(64) NOT NULL COMMENT 'port',  
  7. TYPE INT NOT NULL COMMENT 'node type: ACTUAL or CONTAINER',  
  8. LAUNCH_DATE DATE NOT NULL COMMENT 'launch date',  
  9. MODIFIED TIMESTAMP NOT NULL COMMENT 'modified time',  
  10. CREATED TIMESTAMP NOT NULL COMMENT 'created time',  
  11. PRIMARY KEY(ID),  
  12. UNIQUE KEY INDEX_WORKER_NODE(HOST_NAME,PORT,LAUNCH_DATE,TYPE)  
  13. )COMMENT='DB WorkerID Assigner for UID Generator',ENGINE = INNODB;  

對應的查詢語句分別是:

 
  1. select * from testTable where PORT=3306 and Type=1  
  2. select * from testTable where PORT=3307 and HOST_NAME='172.21.1.1'  
  3. select * from testTable where PORT=3308 AND HOST_NAME='172.21.1.2' AND TYPE=2  

那么究竟用到了哪些索引呢?

第一句:沒用到索引,在聚集索引上從左至右依次掃描過濾;

第二句:用到了輔助索引INDEX_WORKER_NODE;

第三句:用到了輔助索引INDEX_WORKER_NODE,但是只有HOST_NAME和PORT條件是通過索引完成的,條件TYPE是依次掃描過濾完成的;

為什么呢?

因為輔助索引是B+樹實現的,雖然可以指定多個列,但是每個列的比較優先級不一樣,寫在前面的優先比較。一旦出現遺漏,在B+樹上就無法繼續搜索了(通過補齊等措施解決的除外),因此是按照最左連續匹配來的。既然是在B+樹上搜索,對於條件的比較自然是要求精確匹配(即"="和"IN")。不過順序倒是可以顛倒,因為查詢優化器重排序一下就好了。

回頭來看,第一句,由於缺少HOST_NAME,只能在聚集索引的葉節點上,從左至右的掃描,挨個比對;

第二句,可以直接在輔助索引上查找,被找到的子樹的所有葉節點就是命中的記錄;

第三句,缺少LAUNCH_DATE條件,所以只能先依據HOST_NAME和PROT在輔助索引上查找,找到的主鍵值作為候選記錄,然后到聚集索引上讀取對應記錄,再比較TYPE條件是否滿足。

2. 示例2:假設有如下的一張表:

CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `cid` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `name_cid_INX` (`name`,`cid`),
  KEY `name_INX` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8

隨便建了一個student表做測試。

create INDEX name_cid_INX ON student(name,cid);
create INDEX name_INX ON student(name);

建了兩個索引,故意這樣建的。
執行1:

EXPLAIN SELECT * FROM student WHERE name='小紅'; 

依據mysql索引最左匹配原則,兩個索引都匹配上了,這個沒有問題。。
執行2:

EXPLAIN SELECT * FROM student WHERE cid=1; 

EXPLAIN SELECT * FROM student WHERE   cid=1 AND name='小紅';

為什么還能匹配索引?

講上面問題之前,我先補充一些知識,因為我覺得你對索引理解是狹隘的:
上述你的兩個查詢的explain結果中顯示用到索引的情況類型是不一樣的。,可觀察explain結果中的type字段。你的查詢中分別是:
1. type: index
2. type: ref

解釋:
index:這種類型表示是mysql會對整個該索引進行掃描。要想用到這種類型的索引,對這個索引並無特別要求,只要是索引,或者某個復合索引的一部分,mysql都可能會采用index類型的方式掃描。但是呢,缺點是效率不高,mysql會從索引中的第一個數據一個個的查找到最后一個數據,直到找到符合判斷條件的某個索引。

所以:對於你的第一條語句:
EXPLAIN SELECT * FROM student WHERE   cid=1;

判斷條件是cid=1,而cid是(name,cid)復合索引的一部分,沒有問題,可以進行index類型的索引掃描方式。explain顯示結果使用到了索引,是index類型的方式。

---------------------------------------------------------------------------------------------------------------------------

ref:這種類型表示mysql會根據特定的算法快速查找到某個符合條件的索引,而不是會對索引中每一個數據都進行一 一的掃描判斷,也就是所謂你平常理解的使用索引查詢會更快的取出數據。而要想實現這種查找,索引卻是有要求的,要實現這種能快速查找的算法,索引就要滿足特定的數據結構。簡單說,也就是索引字段的數據必須是有序的,才能實現這種類型的查找,才能利用到索引。

有些了解的人可能會問,索引不都是一個有序排列的數據結構么。不過答案說的還不夠完善,那只是針對單個索引,而復合索引的情況有些同學可能就不太了解了。

下面就說下復合索引:
以該表的(name,cid)復合索引為例,它內部結構簡單說就是下面這樣排列的:

Mysql創建復合索引的規則是首先會對復合索引的最左邊的,也就是第一個name字段的數據進行排序,在第一個字段的排序基礎上,然后再對后面第二個的cid字段進行排序。其實就相當於實現了類似 order by name cid這樣一種排序規則。

所以:第一個name字段是絕對有序的,而第二字段就是無序的了。所以通常情況下,直接使用第二個cid字段進行條件判斷是用不到索引的,當然,可能會出現上面的使用index類型的索引。這就是所謂的mysql為什么要強調最左前綴原則的原因。

那么什么時候才能用到呢?
當然是cid字段的索引數據也是有序的情況下才能使用咯,什么時候才是有序的呢?觀察可知,當然是在name字段是等值匹配的情況下,cid才是有序的。發現沒有,觀察兩個name名字為 c 的cid字段是不是有序的呢。從上往下分別是4 5。
這也就是mysql索引規則中要求復合索引要想使用第二個索引,必須先使用第一個索引的原因。(而且第一個索引必須是等值匹配)。
---------------------------------------------------------------------------------------------------------------------------
所以對於你的這條sql查詢:
EXPLAIN SELECT * FROM student WHERE   cid=1 AND name='小紅';

沒有錯,而且復合索引中的兩個索引字段都能很好的利用到了!因為語句中最左面的name字段進行了等值匹配,所以cid是有序的,也可以利用到索引了。

你可能會問:我建的索引是(name,cid)。而我查詢的語句是cid=1 AND name='小紅'; 我是先查詢cid,再查詢name的,不是先從最左面查的呀?

好吧,我再解釋一下這個問題:首先可以肯定的是把條件判斷反過來變成這樣 name='小紅' and cid=1; 最后所查詢的結果是一樣的。
那么問題產生了?既然結果是一樣的,到底以何種順序的查詢方式最好呢?

所以,而此時那就是我們的mysql查詢優化器該登場了,mysql查詢優化器會判斷糾正這條sql語句該以什么樣的順序執行效率最高,最后才生成真正的執行計划。所以,當然是我們能盡量的利用到索引時的查詢順序效率最高咯,所以mysql查詢優化器會最終以這種順序進行查詢執行。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM