MySQL索引(二)B+樹在磁盤中的存儲


MySQL索引(二)B+樹在磁盤中的存儲

回顧

 

w200

 

上一篇文章《MySQL索引為什么要用B+樹》講了MySQL為什么選擇用B+樹來作為底層存儲結構,提了兩個知識點:

  1. B+樹索引並不能直接找到行,只是找到行所在的頁,通過把整頁讀入內存,再在內存中查找。
  2. 索引的B+樹高度一般為2-4層,查找記錄時最多只需要2-4次IO。

為進一步知其所以然,今天來聊聊B+樹索引在物理磁盤上是怎么設計存儲的。

一、理解為什么要減少磁盤IO次數

眾所周知,MySQL的數據實際是存儲在文件中,而磁盤IO的查找速度是要遠小於內存速度的,所以減少磁盤IO的次數能很大程度的提高MySQL性能。

1.1 磁盤IO為什么慢

先溫習下知識點:磁盤IO時間 = 尋道 + 磁盤旋轉 + 數據傳輸時間

從磁盤讀取數據時,系統會將邏輯地址發給磁盤,磁盤將邏輯地址轉換為物理地址(哪個磁道,哪個扇區)。 磁頭進行機械運動,先找到相應磁道,再找該磁道的對應扇區,扇區是磁盤的最小存儲單元(見圖1-1)。

 

-w356 圖1-1 磁盤物理結構

 

1.2 性能對比

機械硬盤的連續讀寫性能很好,但隨機讀寫性能很差。

  • 順序訪問:內存訪問速度是硬盤訪問速度的6~7倍(kafka的特點,以后有機會的話再講一講)
  • 隨機訪問:內存訪問速度就要比硬盤訪問速度快上10萬倍以上

隨機讀寫時,磁頭需要不停的移動,時間都浪費在了磁頭尋址上。 而在實際的磁盤存儲里,是很少順序存儲的,因為這樣的維護成本會很高。

二、索引在磁盤上的存儲

知道磁盤IO的性能了吧,接下來看看MySQL是如何根據這種情況來設計索引的物理存儲,以下內容以InnoDB引擎為例,MyISAM略有不同,后面再講。

假設我們有一張這樣的表,表中有如圖2-0的數據

CREATE TABLE `user` ( `ID` bigint(11) NOT NULL AUTO_INCREMENT, `NAME` varchar(20), PRIMARY KEY (`ID`), KEY `idx_name` (`NAME`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 復制代碼

 

圖2-0 表數據

 

2.1 聚集索引(Clustered index )

每個InnoDB表都有一個稱為聚集索引的特殊索引,該索引是按照表的主鍵構造的一棵B+樹。

根據示例數據構建如圖2-1所示聚集索引:

 

 圖2-1 B+樹聚集索引

 

2.1.1 知識點

  • 葉子節點存放了整張表的所有行數據。
  • 非葉子節點並不存儲行數據,是為了能存儲更多索引鍵,從而降低B+樹的高度,進而減少IO次數。
  • 聚集索引的存儲在物理上並不是連續的,每個數據頁在不同的磁盤塊,通過一個雙向鏈表來進行連接。

2.1.2 查找:假設要查找數據項6

  1. 把根節點由磁盤塊0加載到內存,發生一次IO,在內存中用二分查找確定6在3和9之間;
  2. 通過指針P2的磁盤地址,將磁盤2加載到內存,發生第二次IO,再在內存中進行二分查找找到6,結束。

這里只進行了兩次IO,實際上,每個磁盤塊大小為4K,3層的B+樹可以表示上百萬的數據,也就是每次查找只需要3次IO,所以索引對性能的提高將是巨大的。

 

 

2.1.3 怎樣選擇聚集索引

每張InnoDB表有且只有一個聚集索引,那它是怎么選擇索引的呢?

  • 一般情況,用PRIMARY KEY來作為聚集索引。
  • 如果沒有定義PRIMARY KEY,將會用第一個UNIQUENOT NULL的列來作為聚集索引。
  • 如果表沒有合適的UNIQUE索引,會內部根據行ID值生成一個隱藏的聚簇索引GEN_CLUST_INDEX

所以在建表的時候,如果沒有邏輯唯一且非空列時,可以添加一個auto_increment的列,方便建立一個聚集索引。

2.2 非聚集索引(Secondary indexes)

非聚集索引又叫輔助索引,葉子節點並不包含行記錄數據,而是存儲了聚集索引鍵。

根據示例數據(idx_name索引)構建如圖2-2所示輔助索引:

 

 圖2-2 B+樹非聚集索引

 

2.2.1 知識點

  • 每個表可以有多個輔助索引
  • 通過輔助索引查數據時,先查找輔助索引獲得聚集索引的主鍵,然后通過主鍵索引來查找完整的行記錄。
  • 通過非主鍵索引比主鍵索引查找速度要慢一倍。

2.2.2 查找:獲取NAME=Jake的數據

第一階段:通過輔助索引查到主鍵索引的主鍵

  1. 把idx_name索引的根節點由磁盤塊0加載到內存,發生一次IO,查找到在P2指針中
  2. 根據P2指針的磁盤地址,加載磁盤塊2到內存,發生第二次IO,查找到Jake節點以及它的主鍵索引9

第二階段:通過主鍵索引找到完整的行記錄

  1. 把根節點由磁盤塊0加載到內存,發生一次IO,在內存中用二分查找確定9在P3指針中
  2. 通過指針P3的磁盤地址,將磁盤3加載到內存,發生第二次IO,再在內存中進行二分查找找到9,以及它的行記錄,

查找結束。


未完待續…

原文鏈接:MySQL索引(二)B+樹在磁盤中的存儲 - 掘金  https://juejin.im/post/5cef2c43e51d45572c05ffe3


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM