深入理解InnoDB(6)—独立表空间
InnoDB的表空間
表空間可以看做是InnoDB存儲(chǔ)引擎邏輯結(jié)構(gòu)的最高層 ,所有的數(shù)據(jù)都是存放在表空間中。
1. Extent
對于16KB的頁來說,連續(xù)的64個(gè)頁就是一個(gè)區(qū),也就是說一個(gè)區(qū)默認(rèn)占用1MB空間大小。
每256個(gè)區(qū)被劃分成一組,第一組的前3個(gè)頁面是固定的(FSP_HDR,IBUF_BITMAP,INODE),每組的前兩個(gè)頁面是固定的(XDES,IBUF_BITMAP)
1.1 為什么需要引入?yún)^(qū)的概念?
因?yàn)锽+樹的每一層的節(jié)點(diǎn),都是用一個(gè)雙向鏈表連起來的,如果以頁作為存儲(chǔ)單位的話,在B+樹上相鄰的兩個(gè)節(jié)點(diǎn),可能在磁盤上相隔非常遠(yuǎn),就會(huì)造成磁盤的隨機(jī)I/O,因此我們應(yīng)該使得相鄰位置的節(jié)點(diǎn),物理位置也盡量相連,形成順序I/O。
所以,所以才引入了區(qū)(extent)的概念,一個(gè)區(qū)就是在物理位置上連續(xù)的64個(gè)頁。在表中數(shù)據(jù)量大的時(shí)候,為某個(gè)索引分配空間的時(shí)候就不再按照頁為單位分配了,而是按照區(qū)為單位分配。
1.2 區(qū)的分類
- FREE 空閑的區(qū)
- FREE_FRAG 有剩余空間的碎片區(qū)
- FULL_FRAG 沒有剩余空間的碎片區(qū)
- FSEG 附屬于某個(gè)段的區(qū)
注意:處于FREE、FREE_FRAG以及FULL_FRAG這三種狀態(tài)的區(qū)都是獨(dú)立的,算是直屬于表空間;而處于FSEG狀態(tài)的區(qū)是附屬于某個(gè)段的。
1.3 XDES Entry
每一個(gè)區(qū)都對應(yīng)著一個(gè)XDES Entry結(jié)構(gòu).XDES Entry的組成如下圖
- Segment ID(8字節(jié))
該區(qū)所屬的段的ID - List Node(12字節(jié))
將XDES Entry連成一個(gè)鏈表,存儲(chǔ)的是指向上一個(gè)XDES和下一個(gè)XDES的指針 - State(4字節(jié))
前面說到的幾種區(qū)的分類,FREE、FREE_FRAG、FULL_FRAG和FSEG - Page State Bitmap(16字節(jié))
這個(gè)部分共占用16個(gè)字節(jié),也就是128個(gè)比特位。我們說一個(gè)區(qū)默認(rèn)有64個(gè)頁,這128個(gè)比特位被劃分為64個(gè)部分,每個(gè)部分2個(gè)比特位,對應(yīng)區(qū)中的一個(gè)頁。這兩個(gè)比特位的第一個(gè)位表示對應(yīng)的頁是否是空閑的,第二個(gè)比特位還沒有用。
1.3.1 XDES Entry鏈表
當(dāng)向某個(gè)段中插入數(shù)據(jù)時(shí)
因此,查找FREE_FRAG 有剩余空間的碎片區(qū),申請一些零散的頁面將數(shù)據(jù)插入,直到為FULL_FRAG。否則找FREE空閑的區(qū),插入數(shù)據(jù)。而為了查找特定狀態(tài)的區(qū):
-
把狀態(tài)為FREE的區(qū)對應(yīng)的XDES Entry結(jié)構(gòu)通過List Node來連接成一個(gè)鏈表,這個(gè)鏈表我們就稱之為FREE鏈表。
-
把狀態(tài)為FREE_FRAG的區(qū)對應(yīng)的XDES Entry結(jié)構(gòu)通過List Node來連接成一個(gè)鏈表,這個(gè)鏈表我們就稱之為FREE_FRAG鏈表。
-
把狀態(tài)為FULL_FRAG的區(qū)對應(yīng)的XDES Entry結(jié)構(gòu)通過List Node來連接成一個(gè)鏈表,這個(gè)鏈表我們就稱之為FULL_FRAG鏈表。
因?yàn)橐粋€(gè)段中可以有好多個(gè)區(qū),有的區(qū)是完全空閑的,有的區(qū)還有一些頁面可以用,有的區(qū)已經(jīng)沒有空閑頁面可以用了,為了找出段中特定狀態(tài)的區(qū):
-
FREE鏈表:同一個(gè)段中,所有頁面都是空閑的區(qū)對應(yīng)的XDES Entry結(jié)構(gòu)會(huì)被加入到這個(gè)鏈表。注意和直屬于表空間的FREE鏈表區(qū)別開了,此處的FREE鏈表是附屬于某個(gè)段的。
-
NOT_FULL鏈表:同一個(gè)段中,仍有空閑空間的區(qū)對應(yīng)的XDES Entry結(jié)構(gòu)會(huì)被加入到這個(gè)鏈表。
-
FULL鏈表:同一個(gè)段中,已經(jīng)沒有空閑空間的區(qū)對應(yīng)的XDES Entry結(jié)構(gòu)會(huì)被加入到這個(gè)鏈表。
1.3.2 鏈表基節(jié)點(diǎn)
上面說到的6種鏈表,都對應(yīng)著一個(gè)LIst Base Node,因此只要記錄下鏈表基節(jié)點(diǎn),就可以搜索整個(gè)鏈表
-
List Length表明該鏈表一共有多少節(jié)點(diǎn),
-
First Node Page Number和First Node Offset表明該鏈表的頭節(jié)點(diǎn)在表空間中的位置。
-
Last Node Page Number和Last Node Offset表明該鏈表的尾節(jié)點(diǎn)在表空間中的位置。
2.segment
段由若干個(gè)零散的頁面以及一些完整的區(qū)組成。
2.1 為什么需要引入段的概念?
在進(jìn)行范圍查詢的時(shí)候,其實(shí)我們是對B+樹的葉子節(jié)點(diǎn)進(jìn)行掃描,如果將B+樹的葉子節(jié)點(diǎn)和非葉子節(jié)點(diǎn)都放在一個(gè)地方的話,就會(huì)影響范圍查詢的速度。
因此,InnoDB將葉子節(jié)點(diǎn)的區(qū)的集合分為一個(gè)段,非葉子區(qū)的集合同樣也分為一個(gè)段。也就是說一個(gè)索引會(huì)生成2個(gè)段,一個(gè)葉子節(jié)點(diǎn)段,一個(gè)非葉子節(jié)點(diǎn)段。
但是這也帶來了一個(gè)問題,就是無論如何一個(gè)索引都要至少要占用兩個(gè)區(qū),就是2M的空間,即使數(shù)據(jù)量遠(yuǎn)小于2M,就會(huì)造成浪費(fèi),因此引入了碎片區(qū)的概念。
2.3 碎片區(qū)
在一個(gè)碎片區(qū)中,并不是所有的頁都是為了存儲(chǔ)同一個(gè)段的數(shù)據(jù)而存在的,而是碎片區(qū)中的頁可以用于不同的目的,比如有些頁用于段A,有些頁用于段B,有些頁甚至哪個(gè)段都不屬于。碎片區(qū)直屬于表空間,并不屬于任何一個(gè)段。所以此后為某個(gè)段分配存儲(chǔ)空間的策略是這樣的:
在剛開始向表中插入數(shù)據(jù)的時(shí)候,段是從某個(gè)碎片區(qū)以單個(gè)頁面為單位來分配存儲(chǔ)空間的。
當(dāng)某個(gè)段已經(jīng)占用了32個(gè)碎片區(qū)頁面之后,就會(huì)以完整的區(qū)為單位來分配存儲(chǔ)空間。
2.4 INODE Entry
與區(qū)的XDES Entry類似,段也存在INODE Entry,組成如下
- Segment ID
就是指這個(gè)INODE Entry結(jié)構(gòu)對應(yīng)的段的編號(hào)(ID)。
- NOT_FULL_N_USED
這個(gè)字段指的是在NOT_FULL鏈表中已經(jīng)使用了多少個(gè)頁面。
- 3個(gè)List Base Node
分別為段的FREE鏈表、NOT_FULL鏈表、FULL鏈表定義的List Base Node,這樣我們想查找某個(gè)段的某個(gè)鏈表的頭節(jié)點(diǎn)和尾節(jié)點(diǎn)的時(shí)候,就可以直接到這個(gè)部分找到對應(yīng)鏈表的List Base Node。
- Magic Number:
這個(gè)值是用來標(biāo)記這個(gè)INODE Entry是否已經(jīng)被初始化了(初始化的意思就是把各個(gè)字段的值都填進(jìn)去了)。如果這個(gè)數(shù)字是值的97937874,表明該INODE Entry已經(jīng)初始化,否則沒有被初始化。。
- Fragment Array Entry
我們前邊強(qiáng)調(diào)過無數(shù)次段是一些零散頁面和一些完整的區(qū)的集合,每個(gè)Fragment Array Entry結(jié)構(gòu)都對應(yīng)著一個(gè)零散的頁面,這個(gè)結(jié)構(gòu)一共4個(gè)字節(jié),表示一個(gè)零散頁面的頁號(hào)
3.FSP_HDR(第一組前3個(gè)頁面之一)
這個(gè)頁面的類型是FSP_HDR,它存儲(chǔ)了表空間的一些整體屬性以及第一個(gè)組內(nèi)256個(gè)區(qū)的對應(yīng)的XDES Entry結(jié)構(gòu)
由圖可得,這個(gè)頁面由五部分組成
3.1 File Header
記錄頁的一些通用信息
3.2 File Space Header
表空間的一些整體屬性信息
- Space ID 4字節(jié) 表空間的ID
- Not Used 4字節(jié) 這4個(gè)字節(jié)未被使用,可以忽略
- Size 4字節(jié) 當(dāng)前表空間占有的頁面數(shù)
- FREE Limit 4字節(jié) 尚未被初始化的最小頁號(hào),大于或等于這個(gè)頁號(hào)的區(qū)對應(yīng)的XDES Entry結(jié)構(gòu)都沒有被加入FREE鏈表
- Space Flags 4字節(jié) 表空間的一些占用存儲(chǔ)空間比較小的屬性
- FRAG_N_USED 4字節(jié) FREE_FRAG鏈表中已使用的頁面數(shù)量
- List Base Node for FREE List 16字節(jié) FREE鏈表的基節(jié)點(diǎn)
- List Base Node for FREE_FRAG List 16字節(jié) FREE_FRAG鏈表的基節(jié)點(diǎn)
- List Base Node for FULL_FRAG List 16字節(jié) FULL_FRAG鏈表的基節(jié)點(diǎn)
- Next Unused Segment ID 8字節(jié) 當(dāng)前表空間中下一個(gè)未使用的 Segment ID
- List Base Node for SEG_INODES_FULL List 16字節(jié) SEG_INODES_FULL鏈表的基節(jié)點(diǎn)
- List Base Node for SEG_INODES_FREE List 16字節(jié) SEG_INODES_FREE鏈表的基節(jié)點(diǎn)
3.3 XDES Entry
在這部分保存的就是一整個(gè)組(256個(gè)區(qū))的XDES Entry,
3.4 Empty Space
用于頁結(jié)構(gòu)的填充,沒啥實(shí)際意義
3.5 File Trailer
校驗(yàn)頁是否完整
4.INODE(第一組前3個(gè)頁面之一)
這個(gè)INODE類型的頁就是為了存儲(chǔ)INODE Entry結(jié)構(gòu)而存在的
結(jié)構(gòu)上與XDES大體相同,但是引入了List Node for INODE Page List
4.1 List Node for INODE Page List
每個(gè)INODE Entry結(jié)構(gòu)占用192字節(jié),一個(gè)頁面里可以存儲(chǔ)85個(gè)這樣的結(jié)構(gòu)。但是一個(gè)表空間中可能存在超過85個(gè)段,所以可能一個(gè)INODE類型的頁面不足以存儲(chǔ)所有的段對應(yīng)的INODE Entry結(jié)構(gòu),所以就需要額外的INODE類型的頁面來存儲(chǔ)這些結(jié)構(gòu)。于是引入了List Node for INODE Page List:
-
SEG_INODES_FULL鏈表:該鏈表中的INODE類型的頁面中已經(jīng)沒有空閑空間來存儲(chǔ)額外的INODE Entry結(jié)構(gòu)了。
-
SEG_INODES_FREE鏈表:該鏈表中的INODE類型的頁面中還有空閑空間來存儲(chǔ)額外的INODE Entry結(jié)構(gòu)了。
因此插入INODE Entry時(shí)
先看看SEG_INODES_FREE鏈表是否為空,如果不為空,直接從該鏈表中獲取一個(gè)節(jié)點(diǎn),也就相當(dāng)于獲取到一個(gè)仍有空閑空間的INODE類型的頁面,然后把該INODE Entry結(jié)構(gòu)放到該頁面中。當(dāng)該頁面中無剩余空間時(shí),就把該頁放到SEG_INODES_FULL鏈表中。
如果SEG_INODES_FREE鏈表為空,則需要從表空間的FREE_FRAG鏈表中申請一個(gè)頁面,修改該頁面的類型為INODE,把該頁面放到SEG_INODES_FREE鏈表中,與此同時(shí)把該INODE Entry結(jié)構(gòu)放入該頁面。
5. XDES類型(每組前2個(gè)頁面之一)
與FSP_HDR類型的頁面對比,除了少了File Space Header部分之外,也就是除了少了記錄表空間整體屬性的部分之外,其余的部分是一樣一樣的。
6. Segment Header
Page Header部分存在
- PAGE_BTR_SEG_LEAF 10字節(jié) B+樹葉子段的頭部信息,僅在B+樹的根頁定義
- PAGE_BTR_SEG_TOP 10字節(jié) B+樹非葉子段的頭部信息,僅在B+樹的根頁定義
二者的10字節(jié)其實(shí)對應(yīng)Segment Header結(jié)構(gòu)
PAGE_BTR_SEG_LEAF記錄著葉子節(jié)點(diǎn)段對應(yīng)的INODE Entry結(jié)構(gòu)的地址是哪個(gè)表空間的哪個(gè)頁面的哪個(gè)偏移量,PAGE_BTR_SEG_TOP記錄著非葉子節(jié)點(diǎn)段對應(yīng)的INODE Entry結(jié)構(gòu)的地址是哪個(gè)表空間的哪個(gè)頁面的哪個(gè)偏移量。
總結(jié)
以上是生活随笔為你收集整理的深入理解InnoDB(6)—独立表空间的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 做梦梦到吃橘子什么意思
- 下一篇: 梦到砍竹子是什么寓意