在我們?nèi)粘i_發(fā)中,針對mysql的sql優(yōu)化,最簡單、最常用的方式就是為查詢條件加索引。那么為什么加個(gè)索引,sql的性能就能有明顯的提升呢?這是因?yàn)獒槍]有索引的sql語句,查詢是在磁盤中全表掃描過濾的,我們知道計(jì)算機(jī)中磁盤性能是最低的,相對于CPU和內(nèi)存來說,慢的可不是一點(diǎn)半點(diǎn)。那么索引中是怎么查詢過濾的呢?索引中其實(shí)是采用了B+樹的方式,在內(nèi)存中過濾完數(shù)據(jù)之后,才到磁盤中獲取具體的數(shù)據(jù),相對于在磁盤中過濾來說,性能當(dāng)然會(huì)提高很多。我們來看一下具體的索引過濾方式。
mysql索引
mysql中的不同存儲(chǔ)引擎的存儲(chǔ)方式是不同的,例如,在InnoDB引擎中使用的是聚簇索引,myISAM使用的是非聚簇索引。關(guān)于聚簇索引和非聚簇索引的區(qū)別我們后面會(huì)說到,這里主要說一下InnoDB中的索引。InnoDB索引類型有很多,大致分為兩大類,一種是主鍵索引,一種是普通索引。之所以分為這兩種,是因?yàn)樗麄冊诓樵冞^濾的過程中不太一樣。其他的索引后面會(huì)講到。
普通索引
如上圖所示,我們看到在普通索引中,索引是以B+樹的形式存儲(chǔ)的,先大概介紹下B+樹。B+樹的每個(gè)節(jié)點(diǎn)可以存儲(chǔ)多個(gè)元素,所有的中間節(jié)點(diǎn)元素都同時(shí)存在于子節(jié)點(diǎn),在子節(jié)點(diǎn)元素中是最大(或最小)元素(如圖中的2就是這樣)。B+樹中的每個(gè)元素不保存數(shù)據(jù)指針,只用來索引,所有數(shù)據(jù)指針都保存在葉子節(jié)點(diǎn),同時(shí)葉子節(jié)點(diǎn)是以鏈表的形式存儲(chǔ)的。在上圖中我們可以看出B+樹范圍查詢,只需要查詢到范圍初始最小節(jié)點(diǎn)的位置,然后在鏈表中遍歷到最大節(jié)點(diǎn)的位置,即可拿到范圍數(shù)據(jù)。具體B+樹流程在這不做詳解,感興趣的可以自行看下資料。
所以使用B+樹的優(yōu)點(diǎn)在于,所有的中間節(jié)點(diǎn)都是存儲(chǔ)的索引,只有葉子節(jié)點(diǎn)存儲(chǔ)的是數(shù)據(jù)的指針,所以,所以磁盤頁中可以存儲(chǔ)更多的節(jié)點(diǎn)元素,也就是說可以讓樹的高度變得更低,來減少IO的次數(shù)。另外因?yàn)锽+樹每次都要查詢到葉子節(jié)點(diǎn)來獲取指針,所以B+樹是穩(wěn)定查找。
我們了解完B+樹之后,可以看到普通索引中的存儲(chǔ)方式跟B+樹中不同的是,它的葉子節(jié)點(diǎn)中存儲(chǔ)的是主鍵id。拿到主鍵之后需要再去主鍵的B+樹中獲取到具體的數(shù)據(jù)信息,這個(gè)過程我們稱之為回表。從這里我們也可以看出,主鍵索引和普通索引的區(qū)別就在于不需要回表。如上圖。
為什么要遵守最左前綴原則
經(jīng)過上面的分析之后,我們就不難理解為什么要準(zhǔn)守最左前綴原則了。比如在聯(lián)合索引的情況下,有name 、age 、sex三個(gè)字段的聯(lián)合索引,為了大家好理解,我簡單畫了下圖。
在上圖中,因?yàn)槲覀兪莕ame、age、sex的組合索引,所以每一個(gè)name、age、sex是一個(gè)節(jié)點(diǎn)。這個(gè)時(shí)候的排序方式就變成了從name開始排序,name排序完成的情況下再依次講age、sex進(jìn)行排序。比如是 2 、8 、5為一個(gè)組合,5 、 3 、2為一個(gè)組合,3 、 6 、5為一個(gè)組合,這幾個(gè)組合是怎么排序的呢?他們的排序順序?yàn)?2 、8 、5 --> 3 、6 、5 --> 5 、3 、2。這個(gè)時(shí)候我們發(fā)現(xiàn)其實(shí)它是首先按照第一個(gè)字段排序的,在保證第一個(gè)字段有序的情況下,再依次對后面的字段進(jìn)行排序。
也就是說在創(chuàng)建聯(lián)合索引(a,b,c)的時(shí)候,只用(a,c)查的話,只能用到a的索引,無法用到c的索引,因?yàn)槁?lián)合索引的B+樹,首先是根據(jù)a來排序的,也就是說a是絕對有序的,b,c基本上是無序的。
總結(jié)
我們先解答開篇,之所以加了索引之后sql語句的效率會(huì)提高,是因?yàn)樗饕牡讓訑?shù)據(jù)結(jié)構(gòu)是B+樹。B+樹的中間節(jié)點(diǎn)都是存儲(chǔ)的索引,只有葉子節(jié)點(diǎn)存儲(chǔ)的是數(shù)據(jù)的指針(普通索引是存儲(chǔ)的id),所以,所以磁盤頁中可以存儲(chǔ)更多的節(jié)點(diǎn)元素,也就是說可以讓樹的高度變得更低,來減少IO的次數(shù)。
另外我們還知道了innodb使用的是聚簇索引,myISAM使用的是非聚簇索引。
聚簇索引又分為主鍵索引和輔助索引,主鍵索引的B+樹中的葉子節(jié)點(diǎn)是直接存儲(chǔ)的數(shù)據(jù)。輔助索引的B+樹葉子節(jié)點(diǎn)中存儲(chǔ)的是主鍵id。非聚簇索引B+樹的葉子節(jié)點(diǎn)中存儲(chǔ)的是數(shù)據(jù)的指針。
還有就是遵守最左前綴原則是因?yàn)?,B+樹中是從最左邊開始排序的,保證最左有序的情況下再依次對后面進(jìn)行排序。所以無論什么情況下最左都是有序的,要知道,B+樹本身就是需要有序才能成立,無序的數(shù)據(jù)是沒辦法獲取的哦!