MySQL面試題:談談MySQL 索引,B+樹原理,以及建索引的幾大原則

推薦學習:MySQL最全整理(面試題+筆記+導圖),面試大廠不再被MySql難倒!

MYSQL一直了解得都不多,之前寫sql準備提交生產環境之前的時候,老員工幫我檢查了下sql,讓修改了一下存儲引擎,當時我使用的是Myisam,後面改成InnoDB了。爲什麼要改成這樣,之前都沒有聽過存儲引擎,於是網上查了一下。

事實上使用不同的存儲引擎也是有很大區別的,下面猿友們可以瞭解一下。

一、存儲引擎的比較

 

 

注:上面提到的B樹索引並沒有指出是B-Tree和B+Tree索引,但是B-樹和B+樹的定義是有區別的。

在 MySQL 中,主要有四種類型的索引,分別爲:B-Tree 索引, Hash 索引, Fulltext 索引和 R-Tree 索引。

B-Tree 索引是 MySQL 數據庫中使用最爲頻繁的索引類型,除了 Archive 存儲引擎之外的其他所有的存儲引擎都支持 B-Tree 索引。Archive 引擎直到 MySQL 5.1 才支持索引,而且只支持索引單個 AUTO_INCREMENT 列。

不僅僅在 MySQL 中是如此,實際上在其他的很多數據庫管理系統中B-Tree 索引也同樣是作爲最主要的索引類型,這主要是因爲 B-Tree 索引的存儲結構在數據庫的數據檢索中有非常優異的表現。

一般來說, MySQL 中的 B-Tree 索引的物理文件大多都是以 Balance Tree 的結構來存儲的,也就是所有實際需要的數據都存放於 Tree 的 Leaf Node(葉子節點) ,而且到任何一個 Leaf Node 的最短路徑的長度都是完全相同的,所以我們大家都稱之爲 B-Tree 索引。

當然,可能各種數據庫(或 MySQL 的各種存儲引擎)在存放自己的 B-Tree 索引的時候會對存儲結構稍作改造。如 Innodb 存儲引擎的 B-Tree 索引實際使用的存儲結構實際上是 B+Tree,也就是在 B-Tree 數據結構的基礎上做了很小的改造,在每一個Leaf Node 上面出了存放索引鍵的相關信息之外,還存儲了指向與該 Leaf Node 相鄰的後一個 LeafNode 的指針信息(增加了順序訪問指針),這主要是爲了加快檢索多個相鄰 Leaf Node 的效率考慮。

InnoDB是Mysql的默認存儲引擎(Mysql5.5.5之前是MyISAM)

可能對於沒有了解過索引的猿友這樣看這篇文章十分吃力,這類猿友有必要先對Mysql索引有個大體的瞭解。

接下來我們先看看B-樹、B+樹的概念。弄清楚,爲什麼加了索引查詢速度會加快?

二、B-樹、B+樹概念

B樹

即二叉搜索樹:

  1. 所有非葉子結點至多擁有兩個兒子(Left和Right);
  2. 所有結點存儲一個關鍵字;
  3. 非葉子結點的左指針指向小於其關鍵字的子樹,右指針指向大於其關鍵字的子樹;

如:

 

 

B-樹

是一種多路搜索樹(並不是二叉的):

  1. 定義任意非葉子結點最多隻有M個兒子;且M>2;
  2. 根結點的兒子數爲[2, M];
  3. 除根結點以外的非葉子結點的兒子數爲[M/2, M];
  4. 每個結點存放至少M/2-1(取上整)和至多M-1個關鍵字;(至少2個關鍵字)
  5. 非葉子結點的關鍵字個數=指向兒子的指針個數-1;
  6. 非葉子結點的關鍵字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];
  7. 非葉子結點的指針:P[1], P[2], …, P[M];其中P[1]指向關鍵字小於K[1]的子樹,P[M]指向關鍵字大於K[M-1]的子樹,其它P[i]指向關鍵字屬於(K[i-1], K[i])的子樹;
  8. 所有葉子結點位於同一層;

如:(M=3)

 

 

B-樹的搜索,從根結點開始,對結點內的關鍵字(有序)序列進行二分查找,如果命中則結束,否則進入查詢關鍵字所屬範圍的兒子結點;重複,直到所對應的兒子指針爲空,或已經是葉子結點;

B-樹的特性:

  1. 關鍵字集合分佈在整顆樹中;
  2. 任何一個關鍵字出現且只出現在一個結點中;
  3. 搜索有可能在非葉子結點結束;
  4. 其搜索性能等價於在關鍵字全集內做一次二分查找;
  5. 自動層次控制;

由於限制了除根結點以外的非葉子結點,至少含有M/2個兒子,確保了結點的至少利用率。

所以B-樹的性能總是等價於二分查找(與M值無關),也就沒有B樹平衡的問題;

由於M/2的限制,在插入結點時,如果結點已滿,需要將結點分裂爲兩個各佔M/2的結點;刪除結點時,需將兩個不足M/2的兄弟結點合併;

B+樹

B+樹是B-樹的變體,也是一種多路搜索樹:

  1. 其定義基本與B-樹同,除了:
  2. 非葉子結點的子樹指針與關鍵字個數相同;
  3. 非葉子結點的子樹指針P[i],指向關鍵字值屬於[K[i], K[i+1])的子樹(B-樹是開區間);
  4. 爲所有葉子結點增加一個鏈指針;
  5. 所有關鍵字都在葉子結點出現;

如:(M=3)

 

 

B+的搜索與B-樹也基本相同,區別是B+樹只有達到葉子結點才命中(B-樹可以在非葉子結點命中),其性能也等價於在關鍵字全集做一次二分查找;

B+的特性:

  1. 所有關鍵字都出現在葉子結點的鏈表中(稠密索引),且鏈表中的關鍵字恰好是有序的;
  2. 不可能在非葉子結點命中;
  3. 非葉子結點相當於是葉子結點的索引(稀疏索引),葉子結點相當於是存儲(關鍵字)數據的數據層;
  4. 更適合文件索引系統;

三、建索引的幾大原則

1.最左前綴匹配原則,非常重要的原則,mysql會一直向右匹配直到遇到範圍查詢(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)順序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引則都可以用到,a,b,d的順序可以任意調整。

2.=和in可以亂序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意順序,mysql的查詢優化器會幫你優化成索引可以識別的形式

3.儘量選擇區分度高的列作爲索引,區分度的公式是count(distinct col)/count(*),表示字段不重複的比例,比例越大我們掃描的記錄數越少,唯一鍵的區分度是1,而一些狀態、性別字段可能在大數據面前區分度就是0,那可能有人會問,這個比例有什麼經驗值嗎?使用場景不同,這個值也很難確定,一般需要join的字段我們都要求是0.1以上,即平均1條掃描10條記錄

4.索引列不能參與計算,保持列“乾淨”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很簡單,b+樹中存的都是數據表中的字段值,但進行檢索時,需要把所有元素都應用函數才能比較,顯然成本太大。所以語句應該寫成create_time = unix_timestamp(’2014-05-29’);

5.儘量的擴展索引,不要新建索引。比如表中已經有a的索引,現在要加(a,b)的索引,那麼只需要修改原來的索引即可

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章