淺析innoDB索引結構
No.1
引言
在上兩期分享了 innoDB鎖機制,innoDB存儲結構,相信大家對innoDB也有了一定的瞭解, 今天將在上期存儲結構的基礎上,詳細聊聊我們平時使用較多的innoDB索引是怎麼回事?
No.2
課前提問
-
爲什麼InnoDB表必須有主鍵,並且推薦使用整型的自增主鍵?沒有主鍵怎麼辦?
-
爲什麼非主鍵索引結構葉子節點存儲的是主鍵值?
-
不考慮其他因素,主鍵長度8B+指針6B,一行數據1k,Innodb引擎三層的索引結構能存多少數據?
No.3
磁盤讀取
爲什麼要講這一章
爲啥子要講磁盤讀取呢,我們不是要講索引結構嗎? 這有因必有果,如果說索引是果的話,那磁盤讀取就是因。相信閱讀過上篇innoDB存儲結構的同學都知道,對於innoDB來講,不管是索引還是數據本身都是以文件的形式存儲在磁盤上的,那麼對於mysql數據的操作本質上就是磁盤I\O,換而言之,磁盤I\O的速度決定了數據庫的操作速度,所以下面我們就來先研究研究磁盤I\O是咋回事。
磁盤構造
先來介紹幾個基本概念
-
磁頭:由圖所示,磁頭就是與盤片磁道直接接觸的東西,通過盤片上的凹凸(0,1)進行數據讀取。
-
磁道:可以理解爲盤片就是由一圈圈磁道組成的,最外側定義爲0磁道,依次向內。
-
扇區:可以理解爲我們用分蛋糕的形式將盤片分割,每一塊就是一個扇區。
-
柱面:由圖可知一個磁盤是由多個磁頭和盤片組成的,主軸旋轉也是多磁頭同步的,所以我們把多盤片的同磁道組成的圓柱面叫做柱面,讀寫數據也是按柱面操作的,寫滿一個柱面纔會寫下一個柱面。
磁盤讀寫流程
磁盤對數據的讀取我們可以大致分爲3步
-
尋道:找到數據所在的磁道(柱面)(此步最耗時,物理結構改變)
-
旋轉:找到對應扇區
-
數據傳輸:和主存交互數據
磁盤I\O時間 = 尋道時間+旋轉時間+數據傳輸時間
順序讀取和隨機讀取
由上述流程可知,對讀取時間影響最大的就是尋道這個步驟,顯然若要用到的數據都在一個磁道扇面的話,那麼僅需一次尋道就可加載完成,若目標數據分佈在盤片的各個磁道上,則需要多次尋道,時間自然更長。
局部性原理
當一個數據被用到時,其附近的數據也通常會馬上被使用。
預讀機制
預讀:頁是計算機管理存儲器的邏輯塊,硬件及操作系統往往將主存和磁盤存儲區分割爲連續的大小相等的塊,每個存儲塊稱爲一頁(在許多操作系統中,頁的大小通常爲4k)
主存和磁盤以頁爲單位交換數據。當程序要讀取的數據不在主存中時,會觸發一個缺頁異常,此時系統會向磁盤發出讀盤信號,磁盤會找到數據所在當前頁並向後連續讀取一頁或幾頁載入內存中,這個操作就叫做預讀。
根據上述的局部性原理,操作系統對於磁盤I\O做了預讀來進行優化,這使得順序讀取的磁盤IO次數進一步減少,從而加快加載速度。
No.4
innoDB的B+樹索引
什麼是索引
MySQL官方對索引的定義爲: 索引(Index)是幫助MySQL高效獲取數據的數據結構。
索引的本質:索引是數據結構,而且是實現了高級查找算法的數據結構
通俗的理解:索引就好比一本書的目錄,可以讓你直接找到目標的頁數。
爲啥要用索引
當有了索引後,我們搜索數據的流程變爲:
-
磁盤讀取索引信息
-
根據索引信息確定目標記錄位置
-
再次進行磁盤IO則可讀取到目標數據
相對於無索引遍歷數據的方式大大減少IO次數,增加讀寫性能。一般使用磁盤I/O次數評價索引結構的優劣。
B+樹數據結構
m階的B+樹定義如下:
-
根節點至少有兩個孩子
-
每個中間節點都包含k個元素和k個孩子,
其中 m/2 <= k <= m
-
每一個葉子節點都包含k-1個元素,
其中 m/2 <= k <= m
-
非葉節點不存儲數據,只存索引
-
所有數據存在葉子節點,有序單鏈表
本文因重點不在於對數據結構的講解,所以爲什麼innoDB選用B+樹而不是選用二叉平衡樹,鏈表等等其他數據結構做索引,B+相對於B樹的優點等問題,請自行學習,或等待以後的分享中講解。
innoDB的B+樹
-
葉子節點爲實際數據,並且爲雙向鏈表
-
每個節點等同於innoDB存儲結構中的一個頁,這也保證了每個節點的數據,物理和邏輯上都在同一個頁中,一個節點的載入就是一個頁的載入。
-
對於innoDB的B+樹來說,樹的階=(一個頁 / 索引字段長度),這也可以解釋爲什麼不推薦索引字段過長,因爲索引字段過長會導致階變小,從而導致樹的高度變高,最終導致索引文件過大。
由此我就可以得出課前提問的第3問的答案,具體流程在文章結尾統一給出。
innoDB基於B+樹的聚集索引
1. 對於innoDB來說 , 數據文件本質上就是B+樹實現的聚集索引文件,葉子節點存儲了具體的行數據,這也就解釋了開篇的問題,innoDB是必須要有主鍵的,若沒有設置innoDB也會默認生成主鍵,但此主鍵不可見.
2. 因爲磁盤由預讀機制,並且innoDB數據頁內物理地址是連續的(頁和頁之間是邏輯連續),所以推薦使用整形的自增主鍵,這樣能保證數據文件的存儲是順序存儲,保證了數據按序填滿數據頁。
innoDB基於B+樹的非聚集索引
對於非主鍵建立的非聚集索引來說,區別在於葉子節點存儲的不在是數據,而是主鍵值。
No.5
數據頁結構(節點)
No.6
根據索引查找數據
innoDB通過索引結構查找數據的流程爲:
以上圖爲例尋找id=11的行數據。
-
磁盤io加載索引段,11<21根據索引確定在p1頁
-
磁盤io加載p1頁,11=11確定數據在p11頁
-
磁盤io加載p11頁數據至主存,通過頁內的稀疏索引進行二分查找到目標數據行
No.7
習題答案
問題一:爲什麼InnoDB表必須有主鍵,並且推薦使用整型的自增主鍵?沒有主鍵怎麼辦?
-
必須有主鍵,因爲數據文件本質是聚集索引文件
-
若未指定innoDB或默認指定隱式主鍵
-
整形自增主鍵,使得innoDB的數據文件在物理結構上數據連續,搭配磁盤IO的預讀,性能更好,並且在檢索時比對索引值效率高,減少樹的高度和分裂次數
問題二:爲什麼非主鍵索引結構葉子節點存儲的是主鍵值?
-
節省空間,行數據只存儲一份,在主鍵形成的聚集索引文件中。
-
提高主鍵複用,保證主鍵索引一致性
問題三:不考慮其他因素,主鍵長度8B+指針6B,一行數據1k,Innodb引擎三層的索引結構能存多少數據?
第一層根節點內的元素數= (16KB/14B)= 1170個
第二層節點數 = 第一層根節點內的元素數 = 1170個
葉子節點數量 = 1170*1170 = 1,368,900個
每個葉子大小 = 頁的大小 = 16KB
每個葉子內行數 = 16KB/1KB = 16條
數據行數 = 葉子節點數量*每個葉子內行數
= 1,368,900*16
= 21,902,400條
END
希望大家踊躍留言討論