索引是一種數據結構,其做用就是用來提升數據查詢效率。比較經常使用的比喻就是將其類比爲書籍的目錄。經過目錄能夠精確的找到某一章節的內容所在頁。數據庫
在數據量較小的時候使用索引其實也沒有什麼意義,即便沒有索引須要一條一條遍歷數據對於計算機來講也並不須要太多時間。而一旦數據量較大,要保證咱們能正常的對外提供服務,保證用戶使用體驗那麼索引就是必要的了。數組
索引時一種數據結構,爲了應對不一樣的場景會有多種實現。在MySQL中主要就是Hash索引和B+Tree。緩存
hash相信你們應該都很熟悉,hash是一種key-value形式的數據結構。實現通常是數組+鏈表的結構,經過hash函數計算出key在數組中的位置,而後若是出現hash衝突就經過鏈表來解決(拉鍊法)。固然還有其餘的解決hash衝突的方法。hash這種數據結構是很經常使用的,好比咱們系統使用HashMap來構建熱點數據緩存,存取效率很好。性能優化
hash結構存數據首先經過計算key的hash值來肯定其在數組中的位置,若是有衝突就在該數組位置建一個鏈表。這樣很明顯有幾個問題:markdown
因此咱們能夠知道的是hash索引適用於快速選取某一行的數據。數據結構
從名字上看這明顯是一種樹結構,在大學期間數據結構的課本上樹結構是必講的。樹結構是一種特別重要的數據結構,在不少地方都會使用到。運維
上面咱們說到hash索引沒法進行範圍查詢,在樹結構中也有一種方便進行有序查詢的結構--二叉搜索樹。二叉搜索樹的結構中要求父節點的值大於左孩子節點而且小於右孩子節點,以下圖。函數
上圖中二叉樹的查詢的時間複雜度爲O(log(n)),固然要保證O(log(n))的時間複雜度就須要保證二叉樹時刻保持平衡。性能
而在MySQL索引中雖然也使用了樹結構,可是並非使用的二叉樹。由於在數據庫中數據最終都是存放在磁盤上的,而若是樹的節點過多的話,那麼在節點之間轉移會花費較多的時間。在MySQL的實現中選擇將更多內容放在同一個節點,對同一個節點的操做轉入在內存中完成,減小在外存中節點之間轉移的次數,以達到提升效率的目的。這就是B+Tree,在B+Tree的實現中一個三層的樹結構就基本上能夠知足咱們幾乎全部的需求了。優化
要了解B+Tree首先就得了解B-Tree,B-Tree是一種平衡樹,這裏的B指的是Balance而不是Binary,更確切的說B-Tree是一種多路平衡搜索樹。
多路平衡搜索樹以下圖:
這是一種2-3樹,意思就是每一個節點存有兩個值,同時每一個節點分支數爲3,從上圖中能夠看出來着中結構很適合查詢數據。每一個節點的左子樹的值都是小於當前節點中最小的值,中間的子樹的值全都是在當前節點兩個值的中間,而右子樹的值全都大於當前節點的最大值。
好比咱們要查找24這個值:
基於上面的流程能夠總結B樹的搜索:
能夠看出其搜索性能至關於在關鍵字集合內作一次二分查找。從這裏看來好像B-Tree沒有什麼問題,可是須要注意到的是在B-Tree中每個節點都是存儲索引關鍵字以及其表明的具體行數據。而在MySQL中數據庫加載數據是以頁爲單位加載,每一頁的大小是固定的(默認16k)。若是每個節點都存儲全部的值,那麼一頁中能存下的節點就會不多,一次查詢可能就會進行屢次從內存中去加載數據,致使性能下降。
B+Tree是對B-Tree的一個變種,讓其更加適應於進行外部存儲文件索引。
二者以前最大的不一樣就在於B-Tree的每一個節點都存儲全部的數據,而B+Tree須要存儲的數據都在葉子節點上,而且增長了順序訪問指針,每一個葉子節點都有指向下一個相鄰的葉子節點的地址。這樣的結構保證了在一個內存頁中能夠存下更多的索引節點,而且更加適合進行範圍查詢。
由於存儲引擎負責實現索引,因此接下來討論索引都是基於MySQL的InnoDB引擎。
聚簇的意思是表示數據行和相鄰的鍵值聚簇的存儲在一塊兒。一些數據庫容許選擇具體的某一個索引做爲聚簇索引,而在InnoDB的實現中直接將主鍵索引指定爲聚簇索引。若是沒有定義主鍵,InnoDB 會選擇一個惟一的非空索引來代替主鍵索引。若是一樣沒有定義這樣的索引,InnoDB會隱式定義一個主鍵來做爲聚簇索引(row_id)。
聚簇索引實例如圖:
在InnoDB中除主鍵索引外其餘都是非聚簇索引,因此也叫非主鍵索引。非主鍵索引的葉節點並非存儲一行的值,而是存儲具體行的主鍵值。不知足聚簇的定義。
非聚簇索引實例如圖:
由上面的兩種索引實例圖就能夠看出來,在查詢時若是是經過主鍵索引查詢的話直接查詢到數據行而後返回。可是若是是經過非主鍵索引查詢的話首先須要經過該索引肯定主鍵,而後經過獲得的主鍵從主鍵索引中查到具體行的數據,後面的經過獲得的主鍵從主鍵索引中獲取數據的過程被稱爲回表。
回表的過程使得經過普通索引查詢較主鍵索引查詢多了一步,在不少狀況下效率相對較低。因此在咱們的查詢過程當中若是可以僅經過主鍵肯定數據那最好就是直接使用主鍵進行查詢。
上面介紹了經過非主鍵查詢會有一個回表的過程,可是須要注意的是並非每個查詢都存在回表這一步,對於一個普通索引來講其葉節點存儲的是主鍵的值,那麼假設我如今須要的數據也僅僅就是主鍵的值呢?經過普通索引取到主鍵的值後就並不須要再到主鍵索引中查,那麼也就不存在回表這一過程了。
上面例子中該非主鍵索引已經存在了咱們所須要的值,因此該索引也被稱爲覆蓋索引。覆蓋索引並非一個固定的結構,可使單索引(一個字段的索引),也可使複合索引,凡是可以直接提供查詢結果而不須要進行回表過程的均可以被稱爲覆蓋索引。
不少時候咱們不可能僅僅經過主鍵來肯定數據,使用普通索引可能會致使低效,因此覆蓋索引在平常開發過程當中也是一個很經常使用的性能優化的手段。
固然覆蓋索引頁並不都是好的,好比我如今創建了一個索引index(a,b)。由a,b兩個字段來創建索引,好處已經說過了就是查詢ab字段時不會回表,可是若是僅僅經過b字段來查詢就沒法走這個索引了。創建的索引的索引項是按照索引定義裏面出現的字段順序排序的。
假設如今存在索引index(a,b),那麼若是經過a和b來查詢可以應用該索引,單獨使用a來查詢也能應用到該索引,可是若是單獨使用b來查詢則沒法應用到該索引。這就是最左前綴原則,在匹配索引時回匹配索引最左邊的n個字段,能匹配上就能夠應用該索引。
因爲最左前綴原則的存在也就要求咱們在創建索引時可能須要考慮更多的事情。
首先須要清楚的事索引是一種數據結構,創建索引時須要消耗存儲空間的,因此索引並非創建的越多越好,而是應該根據需求儘量的減小索引的數量。
而最左前綴原則的存在就使得一個聯合索引能夠被當成多個索引來使用,固然前提是設計好索引中字段的順序(實際上最左前綴原則也並非僅僅適用於聯合索引,對於字符串索引也使用,字符串索引中最左n個字符至關於聯合索引中的最左n個字段)。
好比index(a,b),有了這個索引後咱們就不須要單獨爲a創建索引,因此在設計聯合索引時通常將使用頻率較高的字段放在前面。
而後是將區分度較高的字段靠前,區分度就是字段中值的重複率,重複率越低區分度越高。好比性別就不適合做爲索引,區分度越高的字段通過一次篩選能過濾掉更多的行。
而後還須要考慮的是字段的大小,因爲索引也須要佔據空間因此通常選用較小的字段。
MySQL運維內參:MySQL、Galera、Inception核心原理與最佳實踐