深入理解InnoDB -- 存儲篇

本文分享InnoDB如何規(guī)劃表空間,如何存儲表空間元信息以及用戶數(shù)據(jù)动羽。

思考一個(gè)問題包帚,如果給你一個(gè)文件,讓你存儲MySql的數(shù)據(jù)运吓,你會怎么做渴邦?

下面是一種比較合理的思路。首先把文件劃分成大小相等的塊(InnoDB中的頁)拘哨,每次取一塊使用谋梭。為了管理這些塊信息,我們也拿出一塊空間宅静,存儲每一塊空間的位置章蚣,偏移量站欺,以及已經(jīng)使用和剩余未使用的塊(InnoDB中的FSP HEADER PAGE
姨夹,文件管理頁)
然后根據(jù)不同的邏輯建立對應(yīng)的對象,如索引對象矾策,回滾信息對象(InnoDB中的段)磷账,這些對象從上面分好的塊中申請空間使用,并管理屬于自己的塊贾虽,當(dāng)然逃糟,這些對象信息也需要拿出一塊空間存儲起來(InnoDB中的INODE PAGE)。
這就是InnoDB中段和頁的概念

下面來明確幾個(gè)核心概念

  • 表空間
    InnoDB將所有數(shù)據(jù)(包括表數(shù)據(jù)蓬豁,索引绰咽,回滾信息,插入緩沖索引頁地粪,系統(tǒng)事務(wù)信息取募,二次寫緩沖)邏輯地放在一個(gè)空間中,稱為共享表空間蟆技。
    默認(rèn)表空間的存儲文件為data目錄下的ibdata1玩敏,初始化為10M斗忌。


  • 一個(gè)索引(InnoDB都是B+索引)由兩個(gè)段管理,葉子節(jié)點(diǎn)段(leaf segment)和非葉子節(jié)點(diǎn)段(non leaf segment)
    回滾數(shù)據(jù)也是通過段管理旺聚。

  • 區(qū)
    InnoDB申請空間的最小單位织阳,由連續(xù)頁組成的空間,大小為1MB砰粹,保持不變唧躲。
    InnoDB一次從磁盤中申請4~5個(gè)區(qū)。


  • InnoDB訪問的最小單位伸眶,默認(rèn)16KB惊窖。一個(gè)區(qū)中一共有64個(gè)連續(xù)的頁。
    緩沖池是以頁為管理單位厘贼,每次讀取或刷新一頁數(shù)據(jù)界酒。
    參數(shù): innodb_page_size,可以將頁大小設(shè)置為4K嘴秸,8K.

InnoDB將表空間按Page切分毁欣,這些Page主要分為兩類:存儲表空間元信息的管理頁(如FIL_PAGE_TYPE_FSP_HDR)和存儲表空間用戶數(shù)據(jù)的索引頁(如FIL_PAGE_INDEX,F(xiàn)IL_PAGE_INODE)岳掐。

FIL Header

所有頁都有兩個(gè)統(tǒng)一的結(jié)構(gòu)凭疮,F(xiàn)IL Header,占據(jù)頁面的前38個(gè)字節(jié)串述,F(xiàn)IL Trailer执解,占據(jù)頁面末尾8字節(jié)。

FIL Header結(jié)構(gòu)如下

變量 字節(jié) 描述
FIL_PAGE_SPACE 4 所在表空間ID(space id)
FIL_PAGE_OFFSET 4 該頁在表空間的偏移量(page no)
FIL_PAGE_PREV 4 前驅(qū)節(jié)點(diǎn)的偏移量(僅對索引頁有效)
FIL_PAGE_NEXT 4 后繼節(jié)點(diǎn)的偏移量(僅對索引頁有效)
FIL_PAGE_LSN 8 頁最后刷新到磁盤的LSN
FIL_PAGE_TYPE 2 頁的類型
FIL_PAGE_FILE_FLUSH_LSN 8 僅在第一個(gè)Page(FSP HEADER PAGE)使用纲酗,用來判斷數(shù)據(jù)庫是否正常關(guān)閉
FIL_PAGE_SPACE_ID 8 僅在第一個(gè)Page使用衰腌,保存數(shù)據(jù)庫關(guān)閉時(shí)歸檔重做日志的編號

InnoDB中每一個(gè)表空間都會有一個(gè)唯一的space id,共享表空間的space id就是0觅赊。
每個(gè)頁都有一個(gè)32位序號page no右蕊,稱為偏移量,即離表空間初始位置的偏移量吮螺。因?yàn)槊總€(gè)頁大小為16kb饶囚,所以第0個(gè)頁的偏移量為0,第一個(gè)頁的偏移量為16384鸠补,以此類推萝风。
通過space id和page no,InnoDB可以定位任何一個(gè)頁紫岩。

FIL_PAGE_TYPE標(biāo)志頁的類型规惰,InnoDB常用頁類型如下
FIL_PAGE_TYPE_ALLOCATED:該頁為最新分配
FIL_PAGE_IBUF_BITMAP:Insert Buffer位圖頁
FIL_PAGE_TYPE_SYS:系統(tǒng)頁
FIL_PAGE_TYPE_TRX_SYS:事務(wù)系統(tǒng)數(shù)據(jù)頁
FIL_PAGE_TYPE_FSP_HDR:FSP HEADER PAGE頁
FIL_PAGE_TYPE_XDES:擴(kuò)展描述頁
FIL_PAGE_IBUF_FREE_LIST:Insert Buffer空閑列表頁
FIL_PAGE_UNDO_LOG:Undo Log頁
FIL_PAGE_INDEX:B+樹葉子節(jié)點(diǎn)頁
FIL_PAGE_INODE:B+樹索引節(jié)點(diǎn)頁
FIL_PAGE_TYPE_BLOB:BLOB頁

FIL Trailer

FIL Trailer是在文件末尾的最后8個(gè)字節(jié)被因, 低位4個(gè)字節(jié)是用來表示Page頁中數(shù)據(jù)的checksum卿拴,最后4字節(jié)和FIL Header中的FIL_PAGE_LSN相同
下面說到的頁都有FIL Header衫仑,F(xiàn)IL Trailer,不再重復(fù)說明堕花。

現(xiàn)在看一下關(guān)鍵的關(guān)鍵的管理頁文狱。

FSP HEADER PAGE

表空間第1頁就是文件管理頁FSP HEADER PAGE,存儲表空間關(guān)鍵元數(shù)據(jù)信息缘挽。由FSP HEADER瞄崇、XDES ENTRIES構(gòu)成。

FSP HEADER

FSP HEADER主要存儲表空間元信息壕曼,維護(hù)關(guān)鍵結(jié)構(gòu)分配信息苏研,主要變量如下:

變量 字節(jié) 描述
FSP_SIZE 4 表空間大小,以Page數(shù)量計(jì)算
FSP_FREE_LIMIT 4 當(dāng)前已經(jīng)使用的位置
FSP_FREE 16 空閑區(qū)鏈表
FSP_FREE_FRAG 16 部分可以用碎片區(qū)鏈表
FSP_FULL_FRAG 16 已經(jīng)完全使用的碎片區(qū)鏈表
FSP_SEG_INODES_FULL 16 已經(jīng)完全使用的INODE PAGE鏈表
FSP_SEG_INODES_FREE 16 部分可用的INODE PAGE鏈表

區(qū)具體可以分為區(qū)(extent)和碎片區(qū)(frag extent)
碎片區(qū)是比較特殊的區(qū)腮郊,用于分配碎片頁摹蘑。

XDES ENTRIES

接下來是區(qū)描述符XDES ENTRIES,每個(gè)區(qū)描述符需占用40個(gè)字節(jié)轧飞,用于追蹤64個(gè)頁的使用狀態(tài)衅鹿。
每個(gè)FSP HEADER PAGE只能管理256個(gè)區(qū)的信息(也就是16384個(gè)頁),因此每隔16384個(gè)頁过咬,會有一個(gè)類似FSP HEADER PAGE的Page來描述隨后的區(qū)信息

XDES ENTRIES主要變量如下

變量 字節(jié) 描述
XDES_FLST_NODE 12 維護(hù)鏈表前后節(jié)點(diǎn)信息
XDES_STATE 4 標(biāo)識該區(qū)是屬于FSP_FREE大渤,F(xiàn)SP_FRAG_FREE或FSP_FRAG_FREE_FULL或XDES_SEG(某個(gè)段)
XDES_BITMAP 16 標(biāo)識區(qū)中64個(gè)頁的使用狀態(tài)

XDES_BITMAP使用位圖方式保存,每個(gè)頁的使用狀態(tài)占用2位(預(yù)留一位)掸绞。

一個(gè)區(qū)可以屬于FSP_FREE泵三,F(xiàn)SP_FRAG_FREE或FSP_FRAG_FREE_FULL或者某一個(gè)段。區(qū)的分配實(shí)現(xiàn)了一套類似于借還的機(jī)制衔掸。段向表空間租借區(qū)畜普,只有段退還該空間時(shí)费封,該區(qū)才能重新出現(xiàn)在FSP_FREE/FSP_FULL_FRAG/FSP_FULL中感挥。

INODE PAGE

表空間文件的第3個(gè)page的類型為FIL_PAGE_INODE募逞,管理表空間的段凌埂。

INODE PAGE由SEGMENT INODE組成驱显,每個(gè)SEGMENT INODE為192字節(jié),對應(yīng)一個(gè)段瞳抓。

SEGMENT INODE結(jié)構(gòu)主要變量如下:

變量 字節(jié) 描述
FSEG_FREE 16 未使用的extend鏈表
FSEG_FULL 16 已完全使用的extend鏈表
FSEG_NOT_FULL 16 部分可用的extend鏈表
FSEG_FRAG_ARR[0] 4 碎片頁數(shù)組首頁地址
FSEG_FRAG_ARR[31] 4 碎片頁數(shù)組尾頁地址

為節(jié)省空間埃疫,每個(gè)segment都先從FSP HEADER的FSP_FREE_FRAG中分配32個(gè)碎片頁(FSEG_FRAG_ARR),當(dāng)這些32個(gè)頁面不夠使用時(shí)孩哑,再申請區(qū)栓霜。

每個(gè)INODE PAGE默認(rèn)可存儲85個(gè)SEGMENT INODE。每個(gè)索引使用2個(gè)segment横蜒,分別用于管理葉子節(jié)點(diǎn)和非葉子節(jié)點(diǎn)胳蛮。
所以一個(gè)INODE PAGE最多可以保存42個(gè)索引信息(一個(gè)索引使用兩個(gè)段)销凑。如果表空間有超過42個(gè)索引,則必須再分配一個(gè)INODE PAGE。INODE PAGE的分配是從碎片區(qū)中申請仅炊,但它的位置不是固定的斗幼。為了找到索引的INODE ENTRY,InnoDB定義了SEGMENT HEADER抚垄,結(jié)構(gòu)如下

變量 字節(jié) 描述
FSEG_HDR_SPACE 4 INODE PAGE所在表空間ID
FSEG_HDR_PAGE_NO 4 INODE PAGE所在表空間的偏移量
FSEG_HDR_OFFSET 2 INODE ENTRY在頁的偏移量

對于用戶表蜕窿,其索引的Root Page中保存了兩個(gè)SEGMENT HEADER,分別指向葉子節(jié)點(diǎn)的SEGMENT INODE和非葉子節(jié)點(diǎn)的SEGMENT INODE呆馁。

鏈表結(jié)構(gòu)

InnoDB的鏈表都是雙向鏈表桐经,如FSP HEADER中變量FSP_FREE,F(xiàn)SP_FREE_FRAG浙滤,F(xiàn)SP_FULL_FRAG阴挣,F(xiàn)SP_SEG_INODES_FULL,他們都是鏈表頭結(jié)構(gòu)FLST_BASE_NODE纺腊,維護(hù)了鏈表的頭指針和末尾指針屯吊,

變量 字節(jié) 描述
FLST_LEN 4 鏈表長度
FLST_FIRST 6 鏈表首節(jié)點(diǎn)地址
FLST_LAST 6 鏈表尾節(jié)點(diǎn)地址

它們指向的節(jié)點(diǎn)為XDES ENTRIES的XDES_FLST_NODE,每個(gè)節(jié)點(diǎn)的結(jié)構(gòu)體稱為FLST_NODE

變量 字節(jié) 描述
FLST_PREV 6 鏈表前驅(qū)節(jié)點(diǎn)地址
FLST_NEXT 6 鏈表后繼節(jié)點(diǎn)地址

下面是一個(gè)表空間的示意圖摹菠,請理解該圖

第2個(gè)Page是FIL_PAGE_IBUF_BITMAP盒卸,主要用于跟蹤隨后的每個(gè)PAGE的change buffer信息,使用4個(gè)bit來描述每個(gè)page的change buffer信息次氨。
由于FIL_PAGE_IBUF_BITMAP的空間有限蔽介,同樣每隔256個(gè)Extent Page之后,也會在XDES PAGE之后創(chuàng)建一個(gè)FIL_PAGE_IBUF_BITMAP煮寡。

其他的表空間元信息Page虹蓄,如
FSP_TRX_SYS_PAGE_NO,共享表空間第6個(gè)Page幸撕,記錄了InnoDB重要的事務(wù)系統(tǒng)信息薇组。
FSP_DICT_HDR_PAGE_NO,共享表空間第8個(gè)Page坐儿,存儲了SYS_TABLES律胀,SYS_TABLE_IDS,SYS_COLUMNS貌矿,SYS_INDEXES和SYS_FIELDS等數(shù)據(jù)詞典表的Root Page(b+樹Root節(jié)點(diǎn)所在Page)炭菌。
有興趣的同學(xué)可以自行了解

索引組織表

上面說了InnoDB通過索引頁來存放行記錄,那么這些行記錄是怎么組織的呢
(這里說的索引頁逛漫,包括了B+樹葉子節(jié)點(diǎn)頁FIL_PAGE_INDEX和B+樹索引節(jié)點(diǎn)頁FIL_PAGE_INODE)

聚集索引

InnoDB中黑低,表都是根據(jù)聚集索引順序組織存放的,這種存儲方式的表稱為索引組織表酌毡。
而InnoDB中主鍵索引使用的是B+索引(通過B+樹組織的索引)

當(dāng)我們需要打開一張表時(shí)克握,需要從表空間的數(shù)據(jù)詞典表中加載元數(shù)據(jù)信息蕾管,其中SYS_INDEXES系統(tǒng)表中記錄了用戶表中所有索引Root Page對應(yīng)的page no,進(jìn)而找到B+樹Root Page菩暗,就可以對整個(gè)用戶數(shù)據(jù)B+樹進(jìn)行操作娇掏。

B+是為磁盤和其他直接存取輔助設(shè)備設(shè)計(jì)的一種多路平衡查找樹。
看一個(gè)例子

B+樹有以下特點(diǎn)
1勋眯、B+樹不僅是多叉樹婴梧,而且每個(gè)非葉子節(jié)點(diǎn)只存儲鍵值,不存儲數(shù)據(jù)客蹋,這樣每個(gè)非葉子節(jié)點(diǎn)所能保存的鍵值大大增加塞蹭,可以降低B+的樹深度。
該特性應(yīng)用到索引上讶坯,可以使每次加載的節(jié)點(diǎn)包括更多的索引數(shù)據(jù)番电,也可以減少IO操作(每次讀取樹的下一層都需要一次IO)。
所以B+索引具有高扇出性辆琅,在數(shù)據(jù)庫中漱办,B+樹的高度一般都在2~4層,查找某一個(gè)鍵值的行記錄最多只需要2到4次IO婉烟。

  1. B+樹中娩井,所有數(shù)據(jù)按鍵值的大小順序存放在同一層的葉子節(jié)點(diǎn)上,由各葉子節(jié)點(diǎn)指針進(jìn)行連接似袁。
    每次查找數(shù)據(jù)都需要查找到葉子節(jié)點(diǎn)洞辣,查找次數(shù)都相同,所以查詢速度很穩(wěn)定昙衅。

  2. B+樹所有的葉子節(jié)點(diǎn)數(shù)據(jù)構(gòu)成了一個(gè)有序鏈表扬霜,在查詢大小區(qū)間的數(shù)據(jù)時(shí)候非常方便。
    如上面例子中的B+樹而涉,如果要查詢[22,89]范圍數(shù)據(jù)著瓶,再需要找到鍵值22,再遍歷到數(shù)據(jù)鍵值89就可以了啼县。
    而遍歷所有數(shù)據(jù)材原,只需要遍歷所有的葉子節(jié)點(diǎn)即可,而不需要遍歷每一層數(shù)據(jù)谭羔,這有利于數(shù)據(jù)庫做全表掃描华糖。

注意:B+樹所有的葉子節(jié)點(diǎn)數(shù)據(jù)構(gòu)成了一個(gè)有序鏈表麦向,這個(gè)是邏輯上的有序瘟裸,而非物理存儲是順序(維護(hù)成本過高)。
InnoDB中诵竭,Page的FIL Header維護(hù)了上下Page的偏移量话告,組成雙向鏈表兼搏,而Page中行記錄的記錄頭中維護(hù)了下一行記錄的位置,組成單向鏈表沙郭。

B+樹的查找
類似于二叉查找樹佛呻。起始于根節(jié)點(diǎn),自頂向下遍歷樹病线,根據(jù)目標(biāo)值與鍵值比較結(jié)果向下查找對應(yīng)子樹吓著。
但B+的數(shù)據(jù)都存儲在葉子節(jié)點(diǎn),所以就算某個(gè)非葉子節(jié)點(diǎn)的鍵值與所查的關(guān)鍵字相等時(shí)送挑,并不停止查找绑莺,而是繼續(xù)沿著這個(gè)節(jié)點(diǎn)左邊的指針向下,一直查到該關(guān)鍵字所在的葉子節(jié)點(diǎn)為止惕耕。

B+樹的平衡
對于插入和刪除操作纺裁,B+通過分裂和合并節(jié)點(diǎn)維持平衡(類型紅黑樹的旋轉(zhuǎn)),
InnoDb中B+樹的鍵值和數(shù)據(jù)都存放在Page中司澎,因此Page也需要合并和分裂欺缘,有興趣的同學(xué)可以自行了解。

輔助索引

InnoDB中聚集索引和輔助索引都是B+索引挤安。但輔助索引葉子節(jié)點(diǎn)的數(shù)據(jù)不是存儲實(shí)際的數(shù)據(jù)谚殊,而是主鍵的值。要想拿到實(shí)際的數(shù)據(jù)需要再通過主鍵索引找到對應(yīng)的行記錄然后才能拿到實(shí)際的數(shù)據(jù)蛤铜,這個(gè)過程稱為回表络凿。
如果查詢語句可以從輔助索引(包括聯(lián)合索引)中獲取到所有需要的列,這時(shí)不需要再通過主鍵索引找到對應(yīng)的行記錄昂羡,這種情況稱為覆蓋索引絮记。

聯(lián)合索引

聯(lián)合索引也是B+索引。
聯(lián)合索引中列的順序很重要虐先。

InnoDB首先根據(jù)聯(lián)合索引中最左邊的怨愤、也就是第一列進(jìn)行排序,在第一列排序的基礎(chǔ)上蛹批,再對聯(lián)合索引中后面的第二列進(jìn)行排序撰洗,依此類推。

所以要想使用聯(lián)合索引的第n列腐芍,必須先使用聯(lián)合索引前面的第1列到第n-1列差导。

(group, score),可能出現(xiàn)以下排序(1, 46), (1,58), (2,23), (2,96), (3,25), (3,67)猪勇。
如果要使用該索引的score列设褐,查詢語句必須先使用該索引的group列,如where group = 2 and score = 96,InnoDB通過group查詢后助析,再通過score查詢犀被。
這個(gè)規(guī)則稱為最左前綴匹配原則。

頁結(jié)構(gòu)

下面看一下InnoDB索引頁如何保持用戶數(shù)據(jù)外冀,索引頁由以下部分組成

變量 字節(jié) 描述
Page Header 56 頁頭寡键,記錄頁的一些狀態(tài)信息
Infimun/Supremum Records 26 系統(tǒng)記錄
User Records 不確定 用戶記錄,即行記錄
Free Space 不確定 空閑空間
Page Directory 不確定 頁目錄

Page Header

變量 字節(jié) 描述
PAGE_N_DIR_SLOTS 2 page directory中槽的數(shù)量
PAGE_HEAP_TOP 2 堆中空閑空間的偏移量
PAGE_N_HEAP 2 記錄數(shù)據(jù)數(shù)量雪隧,包含用戶記錄西轩,系統(tǒng)記錄以及標(biāo)記刪除的記錄
PAGE_FREE 2 刪除記錄的鏈表
PAGE_GARBAGE 2 已標(biāo)記刪除記錄數(shù)量
PAGE_N_RECS 2 用戶記錄數(shù)量,不包含系統(tǒng)記錄以及標(biāo)記刪除的記錄
PAGE_MAX_TRX_ID 8 最近一次修改該P(yáng)age記錄的事務(wù)ID
PAGE_LEVEL 2 當(dāng)前頁在索引樹的位置
PAGE_INDEX_ID 8 索引id脑沿,表示當(dāng)前頁屬于那個(gè)索引
PAGE_BTR_SEG_LEAF 10 B+樹葉子節(jié)點(diǎn)所在段的SEGMENT HEADER遭商,僅在B+樹的Root Page中定義
PAGE_BTR_SEG_TOP 10 B+樹非葉子節(jié)點(diǎn)所在段的SEGMENT HEADER,僅在B+樹的Root Page中定義

PAGE_LAST_INSERT捅伤,PAGE_DIRECTION劫流,PAGE_N_DIRECTION等變量并未在表中列出,他們用于進(jìn)行頁的分裂操作丛忆。

當(dāng)記錄被刪除(不僅是將記錄的deleted_flag設(shè)置為1祠汇,而是徹底刪除),會放到PAGE_FREE鏈表中(鏈表通過記錄頭信息next_record串聯(lián))熄诡,如果這個(gè)頁上有記錄要插入可很,會先檢查PAGE_FREE鏈表空間是否滿足,如果空間滿足凰浮,直接從PAGE_FREE鏈表空間分配我抠,如果空間不夠,再從空閑空間(PAGE_HEAP_TOP)分配袜茧。當(dāng)空閑地址不足時(shí)菜拓,會調(diào)用函數(shù)btr_page_reorganize_low進(jìn)行頁的重新組織,即根據(jù)頁中記錄主鍵的順序重新進(jìn)行整理笛厦,這樣就能整理出碎片的空間纳鼎。若還是空間不足,則進(jìn)行分裂操作裳凸。
注意:檢查PAGE_FREE鏈表空間時(shí)贱鄙,僅檢查第一個(gè)節(jié)點(diǎn)的可用空間,不會通過next_record進(jìn)行遍歷姨谷。

頁記錄是根據(jù)主鍵順序排序的逗宁,這個(gè)排序是邏輯上的,而非物理上的(開銷過大)梦湘。

Infimun和Supremum Records
系統(tǒng)虛擬的記錄瞎颗,Infimun表示比任何主鍵值都小的值件甥,Supremum表示比任何可能的值都大的值。

User Records
行記錄以鏈表的形式存放在 User Records 中言缤,行記錄格式中的記錄頭中的 next_record 存放著下一條記錄的地址

Free Space
隨著記錄越來越多嚼蚀,F(xiàn)ree Space空間越來越小禁灼,User Records空間越來越大管挟。當(dāng)Free Space的全部空間都被分配完了,這個(gè)頁也就使用完了弄捕,需要申請新的頁僻孝。

Page Directory
B+索引本身不能定位具體的一條記錄,只能找到該記錄所在的頁守谓。
InnoDB將頁載入到內(nèi)存后穿铆,可以遍歷頁所有的記錄找到目標(biāo)記錄,但這樣做太慢了斋荞。
(Page的記錄非物理順序存儲荞雏,無法通過物理地址二分查詢)

InnoDB將頁中數(shù)據(jù)進(jìn)行分組,將每個(gè)組最后一條數(shù)據(jù)的偏移量按順序存儲起來平酿,組成目錄凤优。
每個(gè)偏移量也被稱為一個(gè)槽(Slot,兩個(gè)字節(jié))蜈彼。這些偏移量都會被存儲到靠近頁的尾部的地方筑辨,被稱為Page Directory。
這樣InnoDB可以通過Page Directory進(jìn)行二叉查找定位目標(biāo)所在分組幸逆,再遍歷該組數(shù)據(jù)就可以棍辕。

每個(gè)槽可以包括4~8條記錄,每個(gè)記錄的記錄頭中n_owned變量还绘,維護(hù)該記錄所在槽的記錄數(shù)量楚昭。
(例外,第1個(gè)槽僅包含一個(gè)1記錄拍顷,即Infimun哪替,最后一個(gè)槽可包含1~8個(gè)記錄)

行格式

上面說了InnoDB索引頁如何保存用戶數(shù)據(jù),即表的行記錄菇怀,下面看看每一行的存儲格式

InnodB中行記錄存儲方法有Compress凭舶,Redundant,Compressed爱沟,Dynamic帅霜。
Redundant行格式是MySql5.0之前使用的,現(xiàn)在基本不會再使用呼伸,這里就不介紹了身冀。

compact格式下钝尸,一行記錄依次為以下內(nèi)容:
變長字段長度列表,NULL標(biāo)志位搂根,記錄頭信息珍促,rowID,TransactionID剩愧,RollPointer猪叙,列1數(shù)據(jù),列2數(shù)據(jù)仁卷,…
其中rowID穴翩,TransactionID,RollPointer由InnoDB生成锦积,
注意芒帕,如果表中已經(jīng)指定主鍵,則不生成rowID丰介。
(TransactionID背蟆,RollPointer用于實(shí)現(xiàn)MVCC功能,將在事務(wù)篇解析)

變長字段長度列表
若列的長度小于255字節(jié)哮幢,用1字節(jié)表示
若列的長度大于255字節(jié)带膀,用2字節(jié)表示(varchar最大限制為65535)

NULL標(biāo)志位
占用字節(jié)為(可為NULL的列數(shù)量/8)向上取整,字節(jié)中哪一位為1,表示該行數(shù)據(jù)對應(yīng)列為NULL值

注意家浇,變長字段長度列表和NULL標(biāo)志位都是是按列定義的倒敘保存的本砰,他們都是可選的,如果表中沒有變長字段和允許NULL值的字段钢悲,那么這兩個(gè)都是占用0字節(jié)長度

char(N)中的N指定的是字符点额,UTF-8下CHAR(10)類型的列,最小可以存儲10字節(jié)的字符莺琳,最大可以存儲30字節(jié)的字符还棱。
所以對于多字節(jié)的字符,char類型在InnoDB存儲引擎內(nèi)部被視為變長字符類型惭等。也意味著這些CHAR數(shù)據(jù)類型的長度會記錄在變長長度列表中珍手。

記錄頭信息
固定5字節(jié),結(jié)構(gòu)如下

變量 字節(jié) 描述
() 2 預(yù)留位
deleted_flag 1 該行是否已被刪除
min_rec_flag 1 如果該行記錄是預(yù)定義為最小的記錄辞做,為1
n_owned 4 該記錄所在Slot擁有的記錄數(shù)
heap_no 13 索引堆中該條記錄的索引號
record_type 3 記錄類型琳要,000(普通),001(B+Tree節(jié)點(diǎn)指針)秤茅,010(Infimum)稚补,011(Supremum)
next_record 16 頁中下一條記錄的相對位置

看一例子

create table mytest (    t1 varchar(10),    t2 varchar(10),    t3 char(10),    t4 varchar(10)) engine=INNODB charset=LATIN1 ROW_format=compact;insert into mytest3 values('d','11',NULL,'fff');

使用vim打開ibd文件,在“命令”模式中輸入“:%!xxd”命令框喳,將文本轉(zhuǎn)換為16進(jìn)制课幕,找到supremum字符串厦坛,后面的就是列數(shù)據(jù)了。
(ibd中可能有多個(gè)頁乍惊,可以依照行的實(shí)際數(shù)據(jù)判斷哪個(gè)是自己要找的數(shù)據(jù)杜秸。)

00010070: 7375 7072 656d 756d 0302 0104 0000 10ff  supremum........00010080: ef00 0000 0002 0100 0000 0010 2281 0000  ............"...00010090: 010a 0110 6431 3166 6666 0000 0000 0000  ....d11fff......

解析如下

03 02 01   // 變長字段長度列表, d列-03润绎,c列-null,不記錄  b列-02  a列-0104  // null標(biāo)準(zhǔn)位, 二進(jìn)制-00000100撬碟,逆序,d,c-null,b,a00 00 10 ff ef  // Record Header凡橱,固定5字節(jié) 00 0000 0002 01  // RowID 小作,InnoDB自動創(chuàng)建亭姥,6字節(jié) 00 0000 0010 22  // TransactionID81 0000 010a 0110  // Roll Pointer64  // 列1數(shù)據(jù)  'a'31 31  // 列2數(shù)據(jù) '11'66 6666  // 列4數(shù)據(jù)  'fff'

行溢出
對于占用字節(jié)數(shù)非常大的列稼钩,在記錄的真實(shí)數(shù)據(jù)中只會存儲一小部分?jǐn)?shù)據(jù)(768個(gè)字節(jié)),剩余的數(shù)據(jù)分散在其他溢出頁中(BLOG類型的頁)达罗,記錄的真實(shí)數(shù)據(jù)中記錄這些頁的地址坝撑,以便找到他們。
注意:行溢出與列定義的類型無關(guān)粮揉。如果varchar過長會發(fā)生行溢出巡李,而text,blog不夠長則不會發(fā)生行溢出扶认。

InnoDB 1.0.x引入新的行格式侨拦,以前支持的Compress 和Redundant稱為Antelope文件格式,新的文件格式為Barracuda文件格式辐宾,有兩個(gè)行記錄格式:Compressed和Dynamic狱从。他們是Compact的變種形式。他們基本沒什么本質(zhì)上的區(qū)別叠纹,唯一的區(qū)別就是對于行溢出的處理不同季研。Compressed在數(shù)據(jù)頁只存儲一個(gè)指向溢出頁的地址,所有的實(shí)際數(shù)據(jù)都存放在溢出頁中誉察。
而Compressed還可以是zlib算法對行數(shù)據(jù)進(jìn)行壓縮与涡,因此對于BLOB,TEXT持偏,VARCHAR這類大長度類型的數(shù)據(jù)能夠非常有效的存儲驼卖。

轉(zhuǎn)載自:

深入理解InnoDB -- 存儲篇

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市鸿秆,隨后出現(xiàn)的幾起案子酌畜,更是在濱河造成了極大的恐慌,老刑警劉巖谬莹,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件檩奠,死亡現(xiàn)場離奇詭異桩了,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)埠戳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進(jìn)店門井誉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人整胃,你說我怎么就攤上這事颗圣。” “怎么了屁使?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵在岂,是天一觀的道長。 經(jīng)常有香客問我蛮寂,道長蔽午,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任酬蹋,我火速辦了婚禮及老,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘范抓。我一直安慰自己骄恶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布匕垫。 她就那樣靜靜地躺著僧鲁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪象泵。 梳的紋絲不亂的頭發(fā)上寞秃,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天,我揣著相機(jī)與錄音单芜,去河邊找鬼蜕该。 笑死,一個(gè)胖子當(dāng)著我的面吹牛洲鸠,可吹牛的內(nèi)容都是我干的堂淡。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼扒腕,長吁一口氣:“原來是場噩夢啊……” “哼绢淀!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起瘾腰,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤皆的,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后蹋盆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體费薄,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡硝全,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了楞抡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伟众。...
    茶點(diǎn)故事閱讀 40,615評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖召廷,靈堂內(nèi)的尸體忽然破棺而出凳厢,到底是詐尸還是另有隱情,我是刑警寧澤竞慢,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布先紫,位于F島的核電站,受9級特大地震影響筹煮,放射性物質(zhì)發(fā)生泄漏遮精。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一寺谤、第九天 我趴在偏房一處隱蔽的房頂上張望仑鸥。 院中可真熱鬧吮播,春花似錦变屁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至环戈,卻和暖如春闷板,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背院塞。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工遮晚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拦止。 一個(gè)月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓县遣,卻偏偏與公主長得像,于是被迫代替她去往敵國和親汹族。 傳聞我的和親對象是個(gè)殘疾皇子萧求,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 本文分享InnoDB如何規(guī)劃表空間,如何存儲表空間元信息以及用戶數(shù)據(jù)顶瞒。 思考一個(gè)問題夸政,如果給你一個(gè)文件,讓你存儲M...
    binecy閱讀 852評論 0 0
  • 事務(wù)的定義 事務(wù)的基本要素(ACID)原子性:Atomicity榴徐,整個(gè)數(shù)據(jù)庫事務(wù)是不可分割的工作單位一致性:Con...
    binecy閱讀 294評論 0 0
  • MySQL InnoDB 引擎現(xiàn)在廣為使用守问,它提供了事務(wù)匀归,行鎖,日志等一系列特性耗帕,本文分析下 InnoDB的內(nèi)部實(shí)...
    勤奮的碼農(nóng)閱讀 366評論 0 2
  • 事務(wù)的定義 事務(wù)的基本要素(ACID)原子性:Atomicity朋譬,整個(gè)數(shù)據(jù)庫事務(wù)是不可分割的工作單位一致性:Con...
    曉碼君閱讀 364評論 0 1
  • MySQL InnoDB 引擎現(xiàn)在廣為使用,它提供了事務(wù)兴垦,行鎖徙赢,日志等一系列特性,本文分析下 InnoDB的內(nèi)部實(shí)...
    __七把刀__閱讀 10,867評論 8 28