每一種解決方案都是爲了解決某一類問題而產生,因此在問爲何使用某種方案的時候,其本質就是在探索該方案是用來知足什麼樣的需求,解決什麼樣的問題。sql
因此探究 InnoDb 爲何使用 B+ 樹這個問題,就是要弄清楚 B+ 樹是用來知足什麼的需求,解決什麼樣的問題。數據庫
咱們先看一下一些經常使用的 SQL 語句數組
# 根據某個肯定值來查詢對應的信息
select id, name, email from user where id = 1;
# 經過區間值查詢
select id, name, email from user where id > 12 and id < 20
# 經過範圍查詢並進行排序
select id, name, email from user where id < 123 order by id desc limit 10;
複製代碼
從以上的幾個經常使用的 SQL 咱們能夠看到在對數據庫進行查找數據的過程當中主要有如下三類需求:數據結構
因此,須要找到一種符合上面全部需求的方案。目前比較經常使用於查詢的數據結構有如下兩種:函數
散列表(哈希表)是根據是一種根據(key, value)直接進行訪問的數據結構,它經過哈希函數將 key 值映射到散列表對應的位置上,查找效率很是高。性能
索引裏其中的一種索引類型哈希索引就是基於散列表實現的,假設咱們對名字創建哈希索引,則查找過程以下圖所示:spa
對於每一行數據,存儲引擎都會對全部的索引列計算一個哈希碼(上圖散列表的位置),散列表裏的每一個元素指向數據行的指針,因爲索引自身只存儲對應的哈希值,因此索引的結構十分緊湊,而且能夠直接根據鍵值直接找到對應的數據記錄,這讓哈希索引查找速度很是快!可是哈希索引也有它的劣勢,具體以下:指針
因此,哈希索引只適用於特定場合,在適當的場景使用,的確能帶來很大的性能提高。好比在 InnoDB 裏,就有一種特殊的功能叫 「自適應哈希索引」,若是 InnoDB 注意到某些索引列值被頻繁使用時,它會在內存基於 B+ 樹索引之上再建立一個哈希索引,這樣就能讓 B+ 樹也具備哈希索引的優勢。code
因此散列表結構沒法知足上文提到的需求。cdn
接着咱們來看看樹。
平衡二叉樹可用於查找,且其查找的時間複雜度近似 O(log2n),可是能夠用平衡二叉樹做爲索引的結構嗎?
答案是不能。
由於數據庫表的數據一般是不少的,正常都是存放在磁盤上的。而磁盤的速度相比內存的速度是慢不少倍的,因此要儘可能減小讀取磁盤的次數,經過從內存讀取數據來提升速度。
那麼,如何將盡可能多且有效的索引數據放到內存中呢?
這裏有兩個問題要解決:
一、儘可能多
讀取磁盤數據的時候,都是按磁盤塊來讀取的(局部性原理與磁盤預讀),並非一條一條的讀。在使用樹這種結構做爲索引的數據結構時,咱們每查找一次數據就須要從磁盤中讀取一個樹節點,也就是對應的一個磁盤塊,因此若是咱們能把儘可能多的數據放到磁盤塊中,那麼每次讀取的數據就會較多。
而平衡二叉樹是每一個節點只存儲一個鍵值和數據,也就是說,存儲的時候,每一個磁盤塊只存儲一個鍵值和數據。
那若是存儲了海量的數據,能夠想象平衡二叉樹的節點將會很是多,樹高也會極其高,在查找數據的時候就會進行不少次磁盤 IO,效率將會極低。
因此平衡二叉樹沒法解決存儲儘可能多的索引到內存中這個問題。
二、有效的索引數據
咱們所說的平衡二叉樹,指的是邏輯結構上的平衡二叉樹,其物理實現是數組。因此在邏輯相近的節點上,其物理位置可能相差會很遠。所以,每次讀取的磁盤頁數據,不少多是用不上的,即有效的索引數據並很少,因此在查找過程當中仍是要進行許屢次的磁盤讀取操做。
因此平衡二叉樹也沒法解決這個問題。
因此,能解決這兩個問題的數據結構 —— B 樹就被髮明出來了。
B 樹(Balance Tree),即平衡樹的意思。B 樹是從平衡二叉樹演化而來,B樹的每一個節點能夠存儲多個關鍵字,它將節點大小設置爲磁盤頁的大小,充分利用了磁盤預讀的功能。每次讀取磁盤頁時就會讀取一整個節點。也正因每一個節點存儲着很是多個關鍵字,樹的深度就會很是的小。進而要執行的磁盤讀取操做次數就會很是少,更多的是在內存中對讀取進來的數據進行查找。B 樹的結構示例以下圖所示:
因爲 B 樹的每個節點,即每個磁盤塊存儲的數據較多,因此必定程度上解決了上文提到的存儲儘可能多的索引的問題。也必定程度上的解決了存儲儘可能多的有效索引的問題。
可是,B 樹只是必定程度上的解決了問題,咱們須要更好的解決問題。即能不能的作到存儲更多的有效的索引呢?
答案是能夠。這時候就就須要 B+ 樹閃亮登場了。
B 樹必定程度上的解決了問題,而從 B 樹演化而來的 B+ 樹能更好的解決問題,因此現實使用中幾乎已經沒有使用 B 樹的狀況了。
B + 樹的結構示意圖以下:
那麼 B+ 樹和 B 樹有哪些不一樣?
由於在數據庫中頁的大小是固定的,InnoDB 中頁的默認大小是 16 KB,若是不存儲數據,那麼節點就能夠存儲更多的鍵值,相應的樹的階樹就會更大,對於一樣的數據量來講,須要的樹高就會變低,樹會更矮胖,如此一來查找數據的時候進行磁盤的 IO 次數就會減小,提高查詢效率。
因爲 B+ 樹的階數等於鍵值數量,假設 B+ 樹的一個節點能夠存儲 1000 個鍵值,那麼 3 層的 B+ 樹 能夠存儲 1000 x 1000 x 1000 = 10億個數據。而且通常根節點是常駐內存的,因此查找 10 億個數據,只須要 2 次磁盤 IO。
B+ 這個特色很好的解決了上文提到的存儲儘可能多的索引數據的問題,而且查詢效率也高。
這個特色使 B+ 樹在實現範圍查找,排序查找,分組查找等操做時變得異常簡單。而 B 樹因爲數據分散在各個節點,要實現這些操做很不容易。
因爲索引數據是按順序排序的,即每次讀取了數據頁的時候,裏面的索引數據大部分都是須要用的,因此也很好的解決了上文提到的如何存儲儘可能多的有效的索引數據的問題。
經過上面的分析,咱們能夠發現,在使用某種解決方案的時候,這種方案必定是用來知足某些需求的,在知足需求的過程當中就會遇到一些問題,而最終的解決方案必定是能儘可能好的解決問題並知足需求的。
因此,探究清楚某種方案是要知足什麼樣的需求,解決什麼樣的問題以及如何的解決了問題,也就明白了爲何使用這個方案。