mysql索引為啥要選擇B+樹 (下)

譚某人發表於2019-03-17

mysql索引為啥要選擇B+樹 (下)

有讀者在 mysql索引為啥要選擇B+樹 (上) 上篇文章中留言總結了選擇 B+ 樹的原因,大體上說對了,今天我們再一起來看看具體的原因。

  • 索引為什麼要儲存在硬碟中

首先要明白幾個概念,伺服器儲存一般分記憶體和硬碟,記憶體的大小相對於硬碟來說是很小的。記憶體的訪問速度是納秒級別的,非常快,而硬碟的訪問速度相對記憶體來說就比較慢了。

不管是訪問記憶體還是硬碟資料,作業系統都是按資料頁來讀取資料的,即每訪問一次硬碟或記憶體,只讀取一頁大小的資料,一頁的大小約等於 4 kb,向硬碟讀取資料的操作叫做磁碟 IO。

看到這裡你或許會知道了 mysql 索引為啥不儲存在記憶體中了吧,一方面是雖然記憶體訪問速度快但容量一般都比較小,存不了多少資料,再一個 mysql 需要讓資料持久化,如果伺服器斷電或異常重啟會導致資料丟失。

  • 怎麼讓二叉搜尋樹支援區間查詢

上篇文章中提到過二叉搜尋樹,為了讓二叉搜尋樹也支援區間查詢,我們把二叉樹的葉子節點通過一個雙向連結串列來連線,並且這個連結串列是有序的,注意葉子節點和普通節點是不一樣的,注意看下面的圖。

mysql索引為啥要選擇B+樹 (下)

因此只需要先找到區間的起始值在連結串列中的位置,然後再往後遍歷,直到遍歷到區間的終止值,即可完成區間查詢。如下圖查詢 7-30 這個區間的資料。

mysql索引為啥要選擇B+樹 (下)

  • 如何提升查詢速度

因為二叉搜尋樹儲存在硬碟中,我們每訪問一個節點,就對應著一次硬碟 IO 操作,上面有說過向硬碟讀取資料速度比較慢。因此樹的高度就代表硬碟 IO 操作的次數,所以我們要想辦法讓樹的高度變矮,來減少硬碟 IO。

要想樹變矮一些,那就把樹多分一些叉來吧,變成一顆多叉樹。下面分別用二叉樹和五叉樹來儲存 16 條資料,看下樹的高度又怎樣的變化。

mysql索引為啥要選擇B+樹 (下)

mysql索引為啥要選擇B+樹 (下)

根節點一般儲存在記憶體中,普通節點和葉子結點儲存在硬碟中,因此顯然二叉樹的高度為 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+ 樹就寫到這了,文中圖片來源於極客時間《資料結構與演算法之美》專欄。對文章如有疑問,歡迎留言交流,如文章有描述不當之處,也希望大家批評指出。如文章對你有幫助,點個贊再走哈,感謝支援。

相關文章