上一篇回顧:數據庫
1.一個索引對應一顆B+樹,全部的真實記錄都是存在葉子節點裏面的,全部的項目錄都存在內節點或者說根節點上。性能
2.innodb會爲咱們的表格主鍵添加一個聚簇索引,若是沒有主鍵的話數據庫是會爲咱們自動添加row_id這一列的。聚簇索引的葉子節點包含完整的用戶記錄。spa
3.咱們是能夠爲本身感興趣的列添加二級索引的,二級索引的葉子節點沒有用戶完整的信息,只是擁有對應列和主鍵的信息,若是想要擁有完整的信息是須要進行回表操做用二級索引找到的主鍵去聚簇索引尋找完整信息。3d
4.B+樹的每一層節點都是按照索引列的大小信息進行排序而組成的雙向鏈表,每一個頁裏裏面的記錄也是按照索引列大小信息組成的單向鏈表。若是是聯合索引的話,先按照前面的列進行排序,若是是相同的狀況下再根據其餘的列進行排序。code
5.每一個索引的搜索都是從根節點進行的,因爲每一個頁面都按照索引列的值創建了Page Directory,因此在肯定了具體頁面信息的狀況下是能夠根據二分法進行快速的定位的。blog
索引的代價:排序
1.空間上的代價:每個索引對應的都是一顆B+樹,B+樹的每個節點都對應的是一個16kb大小的數據頁,若是是一個很大的數據庫的話那麼佔用的內存仍是很大的。索引
2.時間上的代價:咱們在上面講過,每層節點都是按照數據的大小順序進行排列的單向鏈表,每一個頁也是按照大小排列的雙向鏈表。那麼咱們在對數據進行操做的時候必然避免不了的就是數據的遷移,數據頁的刪除,回收,分裂等等,若是咱們建立的索引過多的話那麼對應的問題就是頻繁的須要對這些東西進行操做。那就是浪費時間,給性能拖後腿。內存
B+樹適用的範圍:字符串
1:建立一個咱們這篇文章須要用到的數據表:
咱們建立好表格之後須要注意的問題:
1>咱們是沒有主鍵的,那麼是由數據庫給咱們生成主鍵,而後再根據主鍵建立聚簇索引;
2>咱們本身建立的索引是沒有包含country這個列的,因此咱們索引的葉子節點只包含name,birthday,phon_num的值以及數據庫幫助咱們建立的主鍵row_id;
下面咱們給出的就是這個索引的示意圖:咱們用顏色對內節點以及葉子節點進行了區分,並且咱們必需要注意的就是這是根據name先排序,而後再根據birthday、phon
全值匹配:
若是是咱們進行查詢的數據列和咱們索引全部列的順序都是同樣的話,那麼咱們稱之爲全值匹配,以下所示的查詢:
咱們就能夠利用索引進行快速的肯定name=asiz的位置,而後若是有相同數據的話再根據這個信息進行birthday和phon_num的匹配。由於咱們的索引是現根據name進行排序,再根據birthday和phon_num進行的排序。
可是,若是咱們要是改變了這個順序,首先使用birthday進行查詢的話,那麼就是不能使用這個索引,只能全文檢索了。由於咱們的索引都是先根據name進行排序的。
因此咱們在使用聯合索引的時候必需要嚴格按照順序,至於裏面具體的規則咱們下面在講。
匹配最左邊的列:
1>只包含最左邊的一個列:以下圖所示,這樣也是可使用到咱們的聯合索引的
2>包含左邊的多個列:以下圖所示,這樣操做也是沒問題的
3>若是咱們在查詢的時候沒有使用到最左邊的name列,以下圖所示,這是不能使用索引的,只能進行全文的檢索
注意:
因此咱們在使用聯合索引的時候,務必須要記住的就是必定要使用到第一個列,由於咱們的索引就是按照第一個列最早開始排序的,若是不按照這個規則進行,那麼咱們是不能使用到索引的。並且,就如咱們最後一條查詢而言,咱們在進行完成name的索引之後,在相同狀況下進行phon_num的查詢的時候是不能使用索引的,由於name完成之後是根據birthday進行的索引排序,因此必定要嚴格按照索引定義的順序進行查找。
匹配值前綴:
1>若是咱們在進行字符串的搜素的時候是沒有必要輸入完整的字符串的,就好像咱們的模糊查詢,咱們只須要輸入字符串的前面字母便可獲得篩選的結果,由於B+樹是現根據name進行排序的,咱們只使用前面的部分字符也是能夠進行二分查找迅速定位。
2>若是咱們給定的字符是位於字符串中間,那麼這樣是不可行的,是不能使用索引的,只能進行全文的檢索,以下圖所示:
範圍匹配:
1>咱們的索引也是能夠應用在範圍查詢裏面的,以下圖所示,由於咱們的數據都是在頁內按照單向鏈表進行排列,頁之間是按照雙向鏈表進行排列,因此是能夠很快速獲取到咱們須要的數據:
2>可是咱們在使用多個列的範圍查找的時候咱們只能使用到的是第一列的索引,可是其餘列的索引咱們是使用不到的,由於咱們是根據查詢出來的結果在不一樣的name裏面在進行birthday的篩選,索引是根據相同name的條件下才對birthday進行排序的,以下圖所示:
精確匹配某一列並範圍匹配某一列:
對於同一個索引來講,咱們使用多個列的範圍查詢的時候,只能使用最左邊列的B+樹,其餘列是不能使用的。可是咱們左邊使用的是精確查詢,右邊使用的是範圍查詢,那麼,咱們的右邊也是可使用到B+樹的,以下圖所示:
咱們分析一下上圖:
1>第一部分的name進行的精確匹配固然是可使用到索引的
2>由於咱們name是同樣的,和索引的排序規則一致,因此birthday的範圍搜索也是可使用到B+樹的
3>由於birthday的範圍進行不一樣查找的結果,因此咱們在進行phon_num的查找的時候是不能使用B+樹的。
用於排序:
咱們在使用排序好比說Order by的時候也是可使用到索引的,以下圖所示,具體的規則和咱們進行查詢的時候是同樣的,由於咱們索引就是按照順序已經進行好排序的,因此若是咱們的排序的順序和索引的順序是一致的,那麼徹底沒問題能夠直接取用數據,可是就是一直強調的問題,若是咱們列的順序改變了們就不能在使用B+樹了。
用於分組:
以下所示,咱們在使用group by的時候須要進行分組,這個過程包含了三個部分,第一個是先對name一致的進行分組,第二個在着基礎上在對birthday一致的進行分組,而後最後在基礎上對phon_num一致的進行分組。這就正好和咱們的索引是一致的,因此是可使用到B+樹的,和上面同樣,咱們的順序問題是堅定的不能亂的。
索引的挑選:
1>必須條件:只爲咱們使用到的查詢條件,分組,排序列建立索引。查詢列表裏面的列咱們沒有必要創建索引。
2>基數考慮:若是一個列的差別數據不是不少,咱們稱之爲基數小的列。也就是說全部數據的這個列的數據大部分都相同,那麼就是基數小,這種列不必建立索引。
3>數據類型:咱們知道的是索引列能夠有不少的數據類型,好比說整形數據咱們就有TINYINT、MEDIUMINT
、INT
、BIGINT
,它們所佔用的空間內存確定是不同的,因此咱們挑選數據類型小的類型做爲索引列的數據類型,能夠有效的節約空間,儲存更多的數據,那麼咱們在進行數據取用的時候一次能夠加載更多的數據進入內存,減少IO損耗,同時在CPU層次來講,數據類型越小,查詢處理的速度是越快的。
4>索引字符串的前綴:這個問題咱們在上面其實提到過,咱們在使用索引的時候是能夠的,那麼在建立索引的時候固然也是能夠的,這樣能夠減小不少的內存空間,e並且咱們在作字符串比較的時候若是咱們使用的是前綴那麼比較的時間也是能夠大大進行縮短的。具體的語法以下:
5>儘可能使用聯合索引:由於咱們的每個索引對應的都是一顆B+樹,須要使用時間和空間進行維護的,咱們文章開始就說了索引須要付出的代價。咱們使用聯合索引,是能夠知足不少字段的索引條件的。
6>主鍵插入的順序:記不記得咱們在上邊說的,索引的一個目錄項對應的是一個頁,咱們的數據都是有序的進行單向鏈表的維護,那麼若是咱們的主鍵在後期插入中間的話就涉及到了位置的移動,目錄項的修改,頁面分裂,數據遷移等等問題。因此咱們建議的是讓數據庫給主鍵進行自增生成。
7>避免冗餘重複的索引:不要爲一個列重複的添加多個索引,這樣是很差的,他對效率的提高沒有半點的幫助,可是對空間的消耗確實實打實的。
8>覆蓋索引:好比咱們開始建立的索引是沒有包含country這個列的,若是咱們以下圖所示進行查詢,咱們原本是能夠在索引直接獲得三個列的數據,可是差一個列,這時候就必須用主鍵去聚簇索引進行回表操做了。因此咱們查詢的列最好都是咱們索引的列,也就是說咱們是鼓勵把須要查詢的列明確進行書寫的。