有讀者在 mysql索引爲啥要選擇B+樹 (上) 上篇文章中留言總結了選擇 B+ 樹的緣由,大致上說對了,今天咱們再一塊兒來看看具體的緣由。mysql
首先要明白幾個概念,服務器存儲通常份內存和硬盤,內存的大小相對於硬盤來講是很小的。內存的訪問速度是納秒級別的,很是快,而硬盤的訪問速度相對內存來講就比較慢了。算法
不論是訪問內存仍是硬盤數據,操做系統都是按數據頁來讀取數據的,即每訪問一次硬盤或內存,只讀取一頁大小的數據,一頁的大小約等於 4 kb,向硬盤讀取數據的操做叫作磁盤 IO。sql
看到這裏你或許會知道了 mysql 索引爲啥不保存在內存中了吧,一方面是雖然內存訪問速度快但容量通常都比較小,存不了多少數據,再一個 mysql 須要讓數據持久化,若是服務器斷電或異常重啓會致使數據丟失。數據庫
上篇文章中提到過二叉搜索樹,爲了讓二叉搜索樹也支持區間查詢,咱們把二叉樹的葉子節點經過一個雙向鏈表來鏈接,而且這個鏈表是有序的,注意葉子節點和普通節點是不同的,注意看下面的圖。服務器
所以只須要先找到區間的起始值在鏈表中的位置,而後再日後遍歷,直到遍歷到區間的終止值,便可完成區間查詢。以下圖查找 7-30 這個區間的數據。數據結構
由於二叉搜索樹保存在硬盤中,咱們每訪問一個節點,就對應着一次硬盤 IO 操做,上面有說過向硬盤讀取數據速度比較慢。所以樹的高度就表明硬盤 IO 操做的次數,因此咱們要想辦法讓樹的高度變矮,來減小硬盤 IO。性能
要想樹變矮一些,那就把樹多分一些叉來吧,變成一顆多叉樹。下面分別用二叉樹和五叉樹來存儲 16 條數據,看下樹的高度又怎樣的變化。mysql索引
根節點通常存儲在內存中,普通節點和葉子結點保存在硬盤中,所以顯然二叉樹的高度爲 5,須要 5 次硬盤 IO,而五叉樹的高度爲 2,查詢一個數據只須要 2 次硬盤 IO。操作系統
固然這僅僅是一個小數據的例子,若是有一億條數據,咱們構建一個 100 叉樹,這棵樹的高度也只有 3,所以多叉樹能大大下降硬盤 IO,提高查詢速度。指針
那麼問題又來了,對於相同的數據量,是否是構建的多叉樹的叉越多越好呢,由於叉越多樹的高度就會越矮。
上面有說過操做系是按數據頁大小來訪問硬盤的,每次 IO 只讀取一個數據頁大小的數據,若是要讀取的數據大於一個數據頁,則會致使屢次 IO。所以咱們要儘可能讓每一個節點的數據大小恰好等於一個數據頁大小,即每訪問一個節點只需一次 IO。
上面講的其實都是爲了提升查詢性能的,mysql 一般還有插入和刪除操做的,這裏咱們再簡單說一下 B+ 樹如何處理插入和刪除節點的操做。
這裏咱們把多叉樹稱做 m 叉樹,這個 m 值是經過數據頁大小和節點數計算出來的,儘可能保證每訪問一個節點就是一個數據頁的大小,並且每一個節點最多隻有 m 個子節點。
如今咱們要往數據庫中插入新的數據,即要往 m 叉樹中插入新的節點,這可能就會致使某些節點的子節點個數大於 m,也就會致使該節點大小大於一個數據頁,訪問該節點就須要屢次 IO。
爲了解決這個問題,m 叉樹會把該節點分裂成兩個節點,而後改分裂操做又會致使其父節點的子節點數可能超過 m,咱們再用一樣的方法分裂節點,一直影響到根節點。
刪除操做也是相似的思想,若是有頻繁的刪除節點,就會致使某些節點的子節點過少,就會浪費存儲空間並下降查詢效率。因此就要想辦法讓這些節點合併起來,合併的話就有可能會致使其子節點數超過 m,超過的話就再用上面的分裂方法分裂子節點。
關於節點分裂和合並操做就簡單說這些了,也不畫圖了,知道這個處理思想就行了。
下面再總結一下 B+ 樹:
B+ 樹就是一種多叉樹,是由二叉搜索樹不斷演變過來的,爲了知足區間快速查詢,B+ 樹的葉子節點經過雙向鏈表串聯起來。
這裏使用雙向鏈表是爲了支持順序和倒序查詢,雖然雙向鏈表相對於單向鏈表雖然會浪費一倍的指針空間,可是在硬盤中這點空間幾乎微乎其微,用這點空間換時間是一件很值得的事情。
B+ 樹的子節點數不超過 m 個,同時也不能少於 m/2 個,一旦超過就須要分裂,一旦少於就須要合併。
關於 mysql InnoDB 引擎爲啥要選擇 B+ 樹就寫到這了,文中圖片來源於極客時間《數據結構與算法之美》專欄。對文章若有疑問,歡迎留言交流,如文章有描述不當之處,也但願你們批評指出。如文章對你有幫助,點個贊再走哈,感謝支持。