咱們都是知道數據庫的數據都是存儲在磁盤上的,當咱們程序啓動起來的時候,就至關於一個進程運行在了機器的內存當中。因此當咱們程序要查詢數據時,必需要從內存出來到磁盤裏面去查找數據,而後將數據寫回到內存當中。可是磁盤的io效率是遠不如內存的,全部查找數據的快慢直接影響程序運行的效率。
而數據庫加索引的主要目的就是爲了使用一種合適的數據結構,可使得查詢數據的效率變高,減小磁盤io的次數,提高數據查找的速率,而再也不是愣頭青式的全局遍歷。mysql
若是咱們簡單的想的話,想要快速的查找到數據,感受hash表是最快的,根據key,hash到某個槽位上,直接一次查找就能夠準確的找到數據的位置,這多快呀。可是咱們在作業務時,每每只須要一條的數據需求不多,大部分的需求都是根據必定的條件查詢一部分的數據,這個時候hash顯示不是很合適。sql
咱們再考慮樹,好比二叉樹,平衡二叉樹,紅黑樹,B樹等,他們都是二分查找,找數也快,可是不論是平衡二叉樹仍是優化後的紅黑樹,說到底他們都是二叉樹,當節點多了的時候,它們的高度就會高呀,我找一個數據。根節點不是,那就找下一層,下一層尚未我就再去找下一層,這樣形成的後果就是我找一個數據可能要找好幾回,而每一次都是執行了一次磁盤的io,而咱們的索引的目的就是要減小磁盤io呀,這樣設計可不行。那咱們是否是把高度變矮就能夠了呢?
因此咱們再考慮下B樹。首先簡單介紹下B樹的數據結構:
首先看看B樹的定義。數據庫
因此,根節點的關鍵字數量範圍:1 <= k <= m-1
,非根節點的關鍵字數量範圍:m/2 <= k <= m-1
。數據結構
這裏的m表示階數,階數表示了一個節點最多有多少個孩子節點,因此描述一顆B樹時須要指定它的階數。優化
咱們再舉個例子來講明一下上面的概念,好比這裏有一個5階的B樹,根節點數量範圍:1 <= k <= 4,非根節點數量範圍:2 <= k <= 4。spa
下面,咱們經過一個插入的例子,講解一下B樹的插入過程,接着,再講解一下刪除關鍵字的過程。操作系統
插入的時候,咱們須要記住一個規則:判斷當前結點key的個數是否小於等於m-1,若是知足,直接插入便可,若是不知足,將節點的中間的key將這個節點分爲左右兩部分,中間的節點放到父節點中便可。設計
例子:在5階B樹中,結點最多有4個key,最少有2個key(注意:下面的節點統一用一個節點表示key和value)。指針
插入22時,發現這個節點的關鍵字已經大於4了,因此須要進行分裂,分裂的規則在上面已經講了,分裂以後,以下。code
分裂,獲得下面的。
因此B樹每一層的節點數會變多,相同的數據量的話,B樹會比二叉樹高度更低,須要的io次數就會變少,因此符合咱們的索引需求。那MySQL最後爲何選擇了B+樹呢,比B樹更優的地方在哪裏呢?
咱們先看看B+樹與B樹不一樣的地方:
如圖:
第一點:當非葉子節點只存索引key而不存data時,就可使得非葉子節點的佔用空間變少,相同容量的節點能夠存儲更多的索引,那一樣是三層的B+樹,階數就會變多,就會比B樹存更多的數據。
第二點:B+樹葉子節點存有相鄰葉子節點的指針,想要理解這個指針的好處,咱們的先知道磁盤讀取數據時每每不是嚴格按需讀取,而是每次都會預讀,即便只須要一個字節,磁盤也會從這個位置開始,順序向後讀取必定長度的數據放入內存。這樣作的理論依據是計算機科學中著名的局部性原理:
預讀的長度通常爲頁(page)的整倍數。頁是計算機管理存儲器的邏輯塊,硬件及操做系統每每將主存和磁盤存儲區分割爲連續的大小相等的塊,每一個存儲塊稱爲一頁(在許多操做系統中,頁得大小一般爲4k),主存和磁盤以頁爲單位交換數據。當程序要讀取的數據不在主存中時,會觸發一個缺頁異常,此時系統會向磁盤發出讀盤信號,磁盤會找到數據的起始位置並向後連續讀取一頁或幾頁載入內存中,而後異常返回,程序繼續運行。
如今再看B+樹葉子節點的指針,咱們就明白了它的用處,預讀的時候能夠保證連續讀取的數據有序。
可能還有的同窗提過B*樹,它是在B+樹基礎上,爲非葉子結點也增長鏈表指針。我的以爲沒用B星樹多是以爲不必吧,咱們在非葉子節點又不存data,data都在葉子節點,非葉子節點了鏈表指針用不上。
聚簇索引和非聚簇索引:上面咱們提到B+樹的葉子節點存了索引key的數據data,可是mysql不一樣的引擎存data的選擇是不同的,MyISAM是將索引文件和真實的數據文件分兩個文件各類存放,索引文件中存的data是該索引key對應的數據在數據文件中的地址值,而InnoDB則是將正式的數據存在了葉子節點中。因此聚簇和非聚簇就是區分葉子節點存的data是否是真實的(能夠理解爲葉子節點擠不擠?)
回表:回表也簡單,可是得先明白主鍵索引和普通索引,上面咱們所的葉子節點存真實的數據,那是隻有主鍵索引纔是這麼存的,普通索引它存的data是主鍵索引的key。那這樣咱們就好理解了。好比我如今給一張表的name字段建了個普通索引,我想select * from table where name = 'test',這個時候咱們找到test節點的時候,拿到的key只是這行數據對應的主鍵key,咱們要獲得整行的數據只能拿着這個key再去主鍵索引樹再找一次。這個操做就叫作回表。
最左匹配原則: 當咱們新建了一個組合索引時,好比(name+age),查詢時使用 where name = xx and age = xx時會走組合索引,而where age = xx and name =xx則不會走。這是由於MySQL建立聯合索引的規則是首先會對聯合索引的最左邊第一個字段排序,在第一個字段的排序基礎上,而後在對第二個字段進行排序。