1. 索引建立的原則 用於索引的最好的備選數據列是那些出現在WHERE子句、join子句、ORDER BY或GROUP BY子句中的列。 僅僅出現在SELECT關鍵字后面的輸出數據列列表中的數據列不是很好的備選列 SELECT col_a <- 不是備選列 FROM tbl1 LEFT JOIN tbl2 ON tbl1.col_b = tbl2.col_c <- 備選列 WHERE col_d = expr; <- 備選列 當然,顯示的數據列與WHERE子句中使用的數據列也可能相同。 我們的觀點是輸出列表中的數據列本質上不是用於索引的很好的備選列。 2. 復合索引的建立以及最左前綴原則 索引字符串值的前綴(prefixe)。如果你需要索引一個字符串數據列,那么最好在任何適當的情況下都應該指定前綴長度。 例如,如果有CHAR(200)數據列,如果前面10個或20個字符都不同,就不要索引整個數據列。 索引前面10個或20個字符會節省大量的空間 你可以索引CHAR、VARCHAR、BINARY、VARBINARY、BLOB和TEXT數據列的前綴。 假設你在表的state、city和zip數據列上建立了復合索引。索引中的數據行按照state/city/zip次序排列, 因此它們也會自動地按照state/city和state次序排列。這意味着,即使你在查詢中只指定了state值, 或者指定state和city值,MySQL也可以使用這個索引。因此,這個索引可以被用於搜索如下所示的數據列組合: state, city, zip state, city state MySQL不能利用這個索引來搜索沒有包含在最左前綴的內容。例如,如果你按照city或zip來搜索, 就不會使用到這個索引。如果你搜索給定的state和具體的ZIP代碼(索引的1和3列), 該索引也是不能用於這種組合值的,盡管MySQL可以利用索引來查找匹配的state從而縮小搜索的范圍。 如果你考慮給已經索引過的表添加索引,那么就要考慮你將增加的索引是否是已有的多列索引的最左前綴。 如果是這樣的,不用增加索引,因為已經有了(例如,如果你在state、city和zip上建立了索引,那么沒有必要再增加state的索引)。 3. 實例分析 通過實例理解單列索引、多列索引以及最左前綴原則 實例:現在我們想查出滿足以下條件的用戶id: mysql>SELECT `uid` FROM people WHERE lname`='Liu' AND `fname`='Zhiqun' AND `age`=26 因為我們不想掃描整表,故考慮用索引。 單列索引: ALTER TABLE people ADD INDEX lname (lname); 將lname列建索引,這樣就把范圍限制在lname='Liu'的結果集1上,之后掃描結果集1,產生滿足fname='Zhiqun'的結果集2,再掃描結果集2,找到 age=26的結果集3,即最終結果。 由於建立了lname列的索引,與執行表的完全掃描相比,效率提高了很多,但我們要求掃描的記錄數量仍舊遠遠超過了實際所需 要的。雖然我們可以刪除lname列上的索引,再創建fname或者age 列的索引,但是,不論在哪個列上創建索引搜索效率仍舊相似。 多列索引: ALTER TABLE people ADD INDEX lname_fname_age (lame,fname,age); 為了提高搜索效率,我們需要考慮運用多列索引,由於索引文件以B-Tree格式保存,所以我們不用掃描任何記錄,即可得到最終結果。 注:在mysql中執行查詢時,只能使用一個索引,如果我們在lname,fname,age上分別建索引,執行查詢時,只能使用一個索引,mysql會選擇一個最嚴格(獲得結果集記錄數最少)的索引。 3.最左前綴:顧名思義,就是最左優先,上例中我們創建了lname_fname_age多列索引,相當於創建了(lname)單列索引,(lname,fname)組合索引以及(lname,fname,age)組合索引。 注:在創建多列索引時,要根據業務需求,where子句中使用最頻繁的一列放在最左邊。
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 復制代碼 執行1: EXPLAIN SELECT * FROM student WHERE name='小紅'; 執行2: EXPLAIN SELECT * FROM student WHERE cid=1; EXPLAIN SELECT * FROM student WHERE cid=1 AND name='小紅'; 為什么還能匹配索引? 你的疑問是:sql查詢用到索引的條件是必須要遵守最左前綴原則,為什么上面兩個查詢還能用到索引? --------------------------------------------------------------------------------------------------------------------------- 講上面問題之前,我先補充一些知識,因為我覺得你對索引理解是狹隘的: 上述你的兩個查詢的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查詢優化器會最終以這種順序進行查詢執行。