爲什麼Mongodb索引用B樹,而Mysql用B+樹?

引言

好久沒寫文章了,今天回來重操舊業。
今天講的這個主題,是《面試官:談談你對mysql索引的認識》,裏頭提到的一個坑。

也就是說,如果面試官問的是,爲什麼Mysql中Innodb的索引結構採取B+樹?這個問題時,給自己留一條後路,不要把B樹噴的一文不值。因爲網上有些答案是說,B樹不適合做文件存儲系統的索引結構。如果按照那種答法,自己就給自己挖了一個坑,很難收場。因此,就有了這篇文章的誕生~

文末附面試指南!

正文

這裏的Mysql指的是Innodb的存儲引擎下的索引結構,其他存儲引擎我們暫時不討論。

B樹和B+樹

開頭,我們先回憶一下,B樹和B+樹的結構以及特點,如下所示:
B樹


注意一下B樹的兩個明顯特點
  • 樹內的每個節點都存儲數據

  • 葉子節點之間無指針相鄰


B+樹


注意一下B+樹的兩個明顯特點

  • 數據只出現在葉子節點

  • 所有葉子節點增加了一個鏈指針

針對上面的B+樹和B樹的特點,我們做一個總結
(1)B樹的樹內存儲數據,因此查詢單條數據的時候,B樹的查詢效率不固定,最好的情況是O(1)。我們可以認爲在做單一數據查詢的時候,使用B樹平均性能更好。但是,由於B樹中各節點之間沒有指針相鄰,因此B樹不適合做一些數據遍歷操作。

(2)B+樹的數據只出現在葉子節點上,因此在查詢單條數據的時候,查詢速度非常穩定。因此,在做單一數據的查詢上,其平均性能並不如B樹。但是,B+樹的葉子節點上有指針進行相連,因此在做數據遍歷的時候,只需要對葉子節點進行遍歷即可,這個特性使得B+樹非常適合做範圍查詢。

因此,我們可以做一個推論:沒準是Mysql中數據遍歷操作比較多,所以用B+樹作爲索引結構。而Mongodb是做單一查詢比較多,數據遍歷操作比較少,所以用B樹作爲索引結構。

那麼爲什麼Mysql做數據遍歷操作多?而Mongodb做數據遍歷操作少呢?
因爲Mysql是關係型數據庫,而Mongodb是非關係型數據。

那爲什麼關係型數據庫,做數據遍歷操作多?

而非關係型數據庫,做數據遍歷操作少呢?
我們繼續往下看

關係型VS非關係型

假設,我們此時有兩個邏輯實體:學生(Student)和班級(Class),這兩個邏輯實體之間是一對多的關係。畢竟一個班級有多個學生,一個學生只能屬於一個班級。
關係型數據庫
我們在關係型數據庫中,考慮的是用幾張表來表示這二者之間的實體關係。常見的無外乎是,一對一關係,用一張表就行。一對多關係,用兩張表。多對多關係,用三張表。
那這裏,我們需要用兩張表表示二者之間邏輯關係,如下所示


那我們,此時要查cname1班的班級,有多少學生怎麼辦?
假設cname這列,我們建了索引!
執行SQL,如下所示!
SELECT *
FROM t_student t1, (
        SELECT cid
        FROM t_class
        WHERE cname = '1班'
    ) t2
WHERE t1.cid = t2.cid

而這,就涉及到了數據遍歷操作!

因爲但凡做這種關聯查詢,你躲不開join操作的!既然涉及到了join操作,無外乎從一個表中取一個數據,去另一個表中逐行匹配,如果索引結構是B+樹,葉子節點上是有指針的,能夠極大的提高這種一行一行的匹配速度!

有的人或許會擡槓說,如果我先執行

SELECT cid
FROM t_class
WHERE cname = '1班'

獲得cid後,再去循環執行

SELECT *
FROM t_student
WHERE cid = ...

就可以避開join操作呀?

對此,我想說。你確實避開了join操作,但是你數據遍歷操作還是沒避開。你還是需要在student的這張表的葉子節點上,一遍又一遍的遍歷!

那在非關係型數據庫中,我們如何查詢cname1班的班級,有多少學生?
非關係型數據庫
有人說,你可以這麼設計?也就是弄兩個集合如下所示

然後,執行兩次查詢去獲得結果!一次去class集合查,獲得id後再去student集合查。

確實,這麼設計是可以的,我沒說不行。只是不符合非關係型數據庫的設計初衷。在MongoDB中,根本不推薦這麼設計。雖然,Mongodb中有一個$lookup操作,可以做join查詢。但是理想情況下,這個$lookup操作應該不會經常使用,如果你需要經常使用它,那麼你就使用了錯誤的數據存儲了(數據庫):如果你有相關聯的數據,應該使用關係型數據庫(SQL)。

因此,正規的設計應該如下


假設name這列,我們建了索引!
我只需執行一次語句
db.class.find( { name: '1班' } )

這樣就能查詢出自己想要的結果。

而這,就是一種單一數據查詢!畢竟你不需要去逐行匹配,不涉及遍歷操作,幸運的情況下,有可能一次IO就能夠得到你想要的結果。

因此,由於關係型數據庫和非關係型數據的設計方式上的不同。導致在關係型數據中,遍歷操作比較常見,因此採用B+樹作爲索引,比較合適。而在非關係型數據庫中,單一查詢比較常見,因此採用B樹作爲索引,比較合適。

面試套路

目前套路有如下幾種

套路一

你簡歷寫了mysql,沒寫mongodb!
面試官:"說說mysql索引結構?"
我:"巴拉巴拉"
面試官:"知道爲什麼用B+樹,不用B樹麼?"
這個時候正常的面試者就蒙了,會把B樹的缺點噴一通!於是乎下一問就是
面試官:"其實一些非關係型數據庫,如mongodb用的就是B樹,你知道原因麼?"
然後你就回去等通知了!

套路二

你簡歷寫了mysql,也寫了mongodb!
這種情況更完美!
面試官:"說說mysql索引結構?"
我:"巴拉巴拉"
面試官:"你簡歷寫了Mongodb,有了解過他的索引結構麼?"
我:"巴拉巴拉"
面試官:"爲什麼Mongodb索引用B樹,而Mysql用B+樹?"
然後你就回去等通知了!

套路三

你簡歷既沒寫mysql,沒寫mongodb!
面試官;"如果你來設計數據庫,你會對他的索引用什麼數據結構?"
我:"首先不考慮紅黑樹這類,巴拉巴拉…應該會用B樹或者B+樹。"
面試官;“如果我要設計一個像Mongodb那樣的非關係型數據庫,我要用什麼數據結構當索引比較合適?”
然後你就可以回去等通知了!

上面三個套路都是真實存在的!總之,只要面試官想問這個問題,都可以繞到這個問題上去!

總結

其實這篇文章很早以前就想寫,後來一直耽擱着。今天有時間剛好補上,希望大家有所收穫。

發佈了36 篇原創文章 · 獲贊 264 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章