InnoDB從內分析之區和段(三)

so_easy發表於2020-09-19

前言

還是先看這個圖:
InnoDB從內分析(一)

Segment(段)

段的概念:段是概念上區的分類集合。

Extent(區)

區的概念:由連續的64個16KB的Page組成的大小為1M的空間稱之為區。區是實際的1M物理空間。當一個區用完之後,表空間會申請1M的連續物理空間分配給區。

為什麼要有Extent(區)的概念呢?
雖然頁與頁之間可以通過雙向連結串列連線,但是頁的物理儲存是隨機IO。通過指標去尋找隨機IO可能會浪費大量的時間。熟悉C的話必然知道連結串列與陣列的區別其實就是儲存的方式不同。再而聯想到Redisrawembstr的儲存區別。因而就很容易發現分配的好處了–連續的頁在定址的時候速度更加的快。

為什麼要有Fragment(碎片區)的概念呢?
新建立一張表的時候,MySQL並不會為這個新建立的表分配1M的儲存空間,實際上是隻分配了96KB的大小儲存空間–即6頁。之所以不分配1M的空間是為了防止空間浪費,只有當Fragment寫完了才會去申請完整的1MExtent空間。

表空間的結構圖

不論是系統表空間還是獨立表空間,都可以看成是由若干個區組成的,每256個區被劃分成一組。
InnoDB從內分析之區和段(三)
其中第一個組的extent0的前三個頁面資料結構固定是:

- FSP_HDR:表空間頭
- IBUF_BITMAP- INODE:段的資料結構

其他的extent的前兩個頁面是固定的:

- XDES:區的描述例項
- IBUF_BITMAP

InnoDB從內分析之區和段(三)

INDOE結構示意圖

INODE
資料段:單獨的區的集合存放葉子節點
索引段:單獨的區的集合存放非葉子節點

InnoDB從內分析之區和段(三)

- `FILE Header`:檔案頭
- `List Node for Node Page List``INODE`雙向連結串列
        Prev Node Page Number
        Prev Node Offsct
        Next Node Page Number
        Next Node Offset
- INODE Entry:段實體
- Empty Space:空閒空間
- File Tailer:檔案尾

XDES結構示意圖

XDES Entry(Extent Descriptor Entry)是對區的描述實體,用來記錄區的資訊。
InnoDB從內分析之區和段(三)

- SegmentID:段ID。每個表空間分為`葉子節點段``非葉子結點段`,也可稱為`資料段``索引段`。每個段都有自己的ID,這個值通過`INODE`結構儲存。
- List Node:指向前後`XDES Entry`的指標。
    `Prev Node Page Number`:前一個節點的頁號
    `Prev Node Offset`:前一個節點的相對位置
    `Next Node Page Number`:下一個節點的頁號
    `Next Node Offset`:下一個節點的相對位置
- State:區的狀態。
    FREE:直屬於表空間-空閒區。
    FREE_FRAG:直屬於表空間-有剩餘空間的碎片區。
    FULL_FRAG:直屬於表空間-沒有剩餘空間的碎片區。
    FSEG:附屬於某個段的區
- Page State Bitmap:1M的連續空間分配給了64`Page`16B=128bit,每2個bit表示1`Page`的狀態。`00`表示空閒,`01`表示已寫。

INODE Entry結構示意圖

InnoDB從內分析之區和段(三)

- `Segment ID`:段ID
- `NOT_FULL_N_USED`:在`NOT_FULL`連結串列中已經使用了多少個頁面
- `List Base Node For FREE List`:完全沒有被使用並分配給該`Segment``Extent`連結串列。
- `List Base Node For NOT_FULL List`:至少有一個`Page`分配給當前`Segment``Extent`連結串列,全部用完時,轉移到`FSEG_FULL`,全部釋放時,則歸還`FSP_FREE`- `List Base Node For FULL List`:分配給當前`Segment``Page`完全使用完的`Extent`連結串列。
- `Magic Number`:標記這個`INODE Entry`是否已經被初始化,初始化的意思就是把各個欄位的值都填進去了。
- `Fragment Array Entry`:指向某個`Page`的指標,記錄的是相對位置。

FSP_HDR的結構示意圖

InnoDB從內分析之區和段(三)

- `FILE Header`:檔案頭
- `File Space Header`:表空間的頭資訊
- `XDES Entry`:區描述例項。256個區別劃分成了一組,組內的每一個區都對應一個`XDES Entry`- `Empty Space`:空閒區域
- `File Traile`:檔案尾部

FIle Space Header結構示意圖

InnoDB從內分析之區和段(三)

- `Space ID`:表空間ID
- `Not Used`:保留欄位,未使用
- `Size`:當前表空間總的`Page`個數
- `FREE Limit`:當前尚未初始化的最小`Page No`。從該`Page`往後的都尚未加入到`表空間``FREE LIST`- `Space Flags`- `FRAG_N_USED``FREE_FRAG`連結串列上已被使用的`Page`數,用於快速計算該連結串列可用空閒`Page`- `List Base Node For FREE List`(簡寫`FSP_FREE`):當一個`Extent`中的所有`Page`都未被使用,放到該連結串列上,用於隨後的分配
- `List Base Node For FREE_FRAG List`(簡寫`FSP_FREE_FRAG`)`FREE_FRAG`連結串列的基點,通常這樣的`Extent`中的`Page`可能歸屬於不同的`segment`,用於`segment frag array page`的分配
- `List Base Node For FULL_FRAG List`(簡寫`FSP_FULL_FRAG`)`Extent`中所有的`Page`都被使用時,放到該連結串列上。當有`Page`從該`Extent`釋放時,則移回`FREE_FRAG`連結串列
- `Next Unused Segment ID`:當前檔案中最大的段ID+1,用於段分配時的seg id計數器
- `List Base Node for SEG_INODES_FULL`:已被完全用滿的`Inode page`連結串列
- `List Base Node for SEG_INODES_FREE`:至少存在一個空閒的`Inode Entry``Inode Page`被放到該連結串列

資料結構分析

都是自己總結琢磨出來的,所以有錯誤的地方希望指正,非常感謝!

從每個結構來看似乎還是很清晰的,就猶如表空間的資料結構圖把自己的每一個部分的細微結構都展現了出來。但是這些結構是如何組合起來工作的,卻一下子摸不著頭腦。接下來就慢慢的分析這些資料結構之間的關係。

首先:聊一聊碎片區

前面提到過一個表剛被建立的時候只有96KB,也即6個頁面。其中這6個頁面是哪些頁面的組成呢?
InnoDB從內分析之區和段(三)

  • File Space Header:表空間頭
  • Insert Buffer Bitmap:表空間的IBUF_BITMAP
  • B-tree Node:這是一個Page,表示的是葉子節點
  • File Segment inode:表空間的INODE頁面。
  • Freshly Allocated Page:剩下2個Page還未被使用。

(1)剛建立的表不會分配完整的Extent,表中的資料開始都是儲存在Fragment碎片區。表空間除了固定的3個Page之外,剩下的就是3個空閒的Page。這3個空閒的Page一開始建表的時候是申請的File Space Header.FSP_FREE_FRAG連結串列指向的碎片區(還有空閒Page的Extent)。如果3個空閒Page被寫滿,這些頁面的頁號會被依次寫在INODE.INode Entry.Fragment Array Entry:0-2,並且向FSP_FREE_FRAG指向的的碎片區申請新的Page,這個新的Page的頁號被寫在Fragment Array Entry 3。—如此下去就會把INODE.INode Entry的32個Fragment用完,接下來就是分配完整的Extent。如果碎片區的空閒頁面被分配完畢,則這個碎片區就會被連結到FSP_FULL_FRAG。反之如果FSP_FULL_FRAG有空閒的頁面則又會被連結回FSP_FREE_FRAG
(2)FSP_FREE_FRAG碎片區最開始是從FSP_FREE中申請的。
(3)碎片區用完之後,開始申請完整的1MExtent。這個Extent也是從FSP_FREE申請而得。每一個Extent都對應了一個XDES Entry,所以這個XDES Entry就會記錄當前的段ID,並且通過XDES Entry.State記錄當前的狀態。通過XDES Entry.Page State Bitmap記錄該Extent每個Page的使用情況。
(4)256個Extent被視為一組,每個Extent都有唯一對應的XDES Entry,新申請的FSP_FREEExtent也不例外。只是這個時候它的stateFREE,所有的Extent申請都是來自於它。如上第(3)種情況申請的Extent,它的stateFSEGExtent處於碎片區並且在FSP_FREE_FRAG連結串列下則stateFREE_FRAGExtent處於碎片區並且在FSP_FULL_FRAG連結串列下則stateFULL_FRAG

經過上面的講解,我當是已經理解了碎片區FSP_FREE_FRAGFSP_FULL_FRAGFragment Array Entry概念了。

再而:聊一聊段

一個索引有兩個段:資料段索引段資料段指的就是單獨存放葉子節點的區連結串列,而索引段指的就是單獨存放非葉子結點的區連結串列。該如何去理解呢?

如上的概述也不知道在哪裡看到的了,而且也得不到確切的真實資料作為補充論證。如果可以像之前講RowPage似的可以分析,那真是再好不過了。竟然已經學瞭如何分析,那就學以致用吧,一起去看看如何分析和求證。

第一步:通過py_innodb_page_info.py檢查ibd

bash-3.2# python py_innodb_page_info.py ../lottery_match/league_map.ibd -v
page offset 00000000, page type <File Space Header>
page offset 00000001, page type <Insert Buffer Bitmap>
page offset 00000002, page type <File Segment inode>
page offset 00000003, page type <B-tree Node>, page level <0001>
page offset 00000004, page type <B-tree Node>, page level <0000>
page offset 00000005, page type <B-tree Node>, page level <0000>
page offset 00000006, page type <B-tree Node>, page level <0000>
page offset 00000007, page type <B-tree Node>, page level <0000>
page offset 00000000, page type <Freshly Allocated Page>
Total number of page: 9:
Freshly Allocated Page: 1
Insert Buffer Bitmap: 1
File Space Header: 1
B-tree Node: 5
File Segment inode: 1

如上存在葉子節點非葉子節點葉子節點page offset 3非葉子節點在page offset 4、5、6、7File Segment inodepage offset 2。這次的重點分析物件是INDOEINode Entry,求證一個索引分兩段的儲存關係是如何儲存的。這個的求證結果可能要分多次求證。

第二步:開始分析page offset 2-INDOE

INODE二進位制分析
求證得到的結論就是:

  • 一個索引分兩段,最開始的時候就以INode Entry結構儲存了段的例項。

可是:單獨存放葉子節點的區連結串列,和單獨存放非葉子節點的區連結串列又該如何解釋和驗證呢?

第三步:分析資料大於1M的ibd檔案

本作品採用《CC 協議》,轉載必須註明作者和本文連結
阿門阿前一棵葡萄樹 阿嫩阿嫩綠地剛發芽 蝸牛揹著那重重的殼呀 一步一步地往上爬

相關文章