InnoDB設計了多種頁結構用於存放不同類型的數據,我們現在主要研究存放數據的頁,稱為索引頁或數據頁。
每個頁由七部分組成,大致功能如下:
- FIleHeader 文件頭:記錄頁的通用信息,比如上下頁的頁號,頁類型,所有的數據頁其實是一個雙鏈表
- PageHeader 頁頭:記錄本頁存儲記錄的狀態信息,比如本頁記錄數量,槽數量
- Infimum + supremum 最小與最大記錄,是虛擬記錄
- User Records 真正存數據的地方:以鏈表的形式存儲一條條行記錄
- Free Space 存數據空間中尚未使用的區域
- Page Directory 頁目錄:頁中某些記錄的相對位置,用於提升查詢效率
- File Trailer 文件尾:刷盤時校驗頁是否完整
其中User Records和Page Directory是我們的主要研究目標。
User Records
其實從一開始是沒有user records這個空間的。當插入第一條數據的時候,會從free space空間分配出一個空間到user records,直到插入最后一條記錄將free space的空間全部用完就會再去申請一個新的頁。
User Records是用來存儲數據的地方,簡單來說就是怎么把每行數據擺在這個空間里。
行格式的數據結構中的記錄頭信息在擺放數據的過程中發揮了重要作用,我們來回顧一下記錄頭信息的各個屬性。:
-
delete_mask 標記該記錄是否被刪除
-
n_owned 如果當前記錄是組內最大記錄,則代表槽內的記錄數
-
heap_no 當前記錄在本頁中的位置信息
-
record_type 表示當前記錄的類型
0表示普通記錄,1表示B+樹非葉子節點記錄,2表示最小記錄,3表示最大記錄
-
next_record 表示當前記錄到下一條記錄的地址偏移量
我們發現有一個next_record記錄了當前記錄到下一個記錄的地地址偏移量,也就是說我們知道了當前記錄的位置就可以找到下一個記錄。所以說,整個user record空間是一個單鏈表。鏈表中的各個節點是按照主鍵值從小到大的順序連接起來的。
Page Directory
現在有一個問題,我們要在一個頁中查找指定的一條記錄。除了從頭遍歷還有更高效率的方法么?Page Directory提供了解決方案。
InnoDB會將一個頁中的所有記錄划分成若干個組,每組4-8個記錄。將每個組最后一個記錄相對於第一個記錄的地址偏移量(可以定位到真實數據記錄)提取出來存放在頁中一個叫做Page Directory
的數組中,數組中的元素就是這些地址偏移量,也稱為槽(slot)。所以Page Directory就是由槽組成的。
所以在一個頁中根據主鍵查找記錄是很快的,步驟為:
- 二分法確定該記錄所在的槽,並找到該槽所在分組中主鍵值最小的那條記錄。
- 通過next_record屬性遍歷單鏈表找到記錄
二分法:只適用於數組。
鏈表是順序存取,不是隨機存取,用二分查找並不能提高查找效率,因為你每次還得從第一個結點出發,找到指針LOW,HIGH,MIDDLE所指的元素,所以一般不在鏈表內使用二分查找。