InnoDB數(shù)據(jù)頁結(jié)構(gòu)分析

自己分析一下ibd文件還是蠻有意思的救赐,能夠?qū)W到不少東西,建議跟著走一遍当犯,慢慢領(lǐng)會作者設(shè)計的意圖
人學(xué)東西總是先感性的認識,慢慢到理性 —— 過程中大腦需要理解和消化

mysql版本5.7.26

?

先貼一張數(shù)據(jù)頁的結(jié)構(gòu)圖-方便對整體有個印象

InnoDB頁結(jié)構(gòu)示意圖
InnoDB頁各組成部分簡單描述

利用工具查看數(shù)據(jù)庫文件ibd的頁分布情況 (工具名py_innodb_page_info)

當你熟悉page的格式后杈曲,自己也能寫一個這樣的工具


test.ibd文件頁分布情況
  • 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>
    以上四個都是數(shù)據(jù)頁
    我們分析 page offset 00000006, page type <B-tree Node>, page level <0000>
    page level <0000> 表示的是葉子節(jié)點

使用十六進制工具Synalyze It! 打開/usr/local/mysql/data/nishui/test.ibd 文件(文件權(quán)限問題此處忽略,二進制工具任意)

找到 00000006 頁在文件中的位置

因為看的是00000006數(shù)據(jù)頁驰凛, 用6 * 16 * 1024 = 98304B (innodb引擎默認一頁16KB(可通過innodb_page_size改變頁大小)担扑,然而1KB= 1024B) 轉(zhuǎn)換成十六進制為 0x18000
show global status like 'Innodb_page_size' 可查看當前頁大小

找到該位置截圖如下:


06頁開始邏輯位置

這個頁便是 00000006 數(shù)據(jù)頁開始的位置了, 可以開始分析詳細數(shù)據(jù)了

一恰响、先分析File Header(38字節(jié)-描述頁信息)

  • 72 08 C8 7F -> 數(shù)據(jù)頁的checksum值
  • 00 00 00 06 -> 頁號(偏移量)
  • 00 00 00 05 -> 前一頁是第5頁
  • FF FF FF FF -> 由于沒有下一頁,因此為該值
  • 00 00 00 00 00 38 23 77 -> 頁的LSN
  • 45 BF -> 頁的類型 0x45BF代表數(shù)據(jù)頁涌献,剛好這頁是數(shù)據(jù)頁
  • 00 00 00 00 00 00 00 00 -> 獨立表空間胚宦,該值為0
  • 00 00 00 5B -> 表空間的SPACE ID

二、分析Page Header(56字節(jié)-記錄頁的狀態(tài)信息)

SHOW TABLE STATUS LIKE 'test'查看表行記錄格式

  • 00 07 -> 代表Page Directory 有7個槽
  • 17 27 -> 代表空閑空間開始位置的偏移量燕垃,即 0x18000 + 0x1727 = 0x19727,觀察這個位置枢劝,這是最后一行的結(jié)束,后面都是空閑的


    06頁空閑邏輯位置
  • 80 1D -> 當前為 Compact 格式卜壕,第15位表示行記錄格式您旁,再加上兩條偽記錄, 因此0x801D - 0x8002 = 0x001B轴捎,代表該頁中實際的記錄有27條記錄
  • 00 00 -> 指向頁中空閑位置(偏移量)
  • 00 00 -> PAGE_GARBAGE 表示沒有刪除的數(shù)據(jù)
  • 16 4A -> PAGE_LAST_INSERT 最后插入記錄的位置偏移 0x18000 + 0x164A = 0x1964A 直接指向最后一行數(shù)據(jù)存儲的地址鹤盒,也就是id為199,這條確實是最后一條插入的


    test表中最后一條記錄圖
  • 00 02 -> PAGE_DIRECTION 最后插入的方向,向右邊插入
  • 00 1A -> PAGE_N_DIRECTION 一個方向連續(xù)插入記錄的數(shù)量 連續(xù)插入26個
  • 00 1B -> PAGE_N_RECS 當前數(shù)據(jù)頁中含有27條記錄
  • 00 00 00 00 00 00 00 00 修改當前頁的最大事務(wù)ID
  • 00 00 -> 代表頁為葉子節(jié)點
  • 00 00 00 00 00 00 00 43 -> 索引ID侦副,表示當前頁屬于哪個索引
  • 00 00 00 42 00 00 00 02 00 F2 -> B+樹數(shù)據(jù)頁非葉子節(jié)點所在段的segment header侦锯。注意該值僅在樹的root頁中定義
  • 00 00 00 42 00 00 00 02 00 32 ->B+樹數(shù)據(jù)頁所在段的segment header。

小結(jié)一下
1.innodb在整個頁可以使用的空間當成heap,當需要插入記錄的時候秦驯,首先會檢查PAGE_FREE指向的空閑空間尺碰,若申請的空間小于等于該空間容量時,那么使用該空閑空間译隘,否者從PAGE_HEAP_TOP指向的空閑空間進行分配
heap中存儲的記錄非物理連續(xù)的亲桥,只是邏輯上連續(xù)的,可用下圖表示
2.PAGE_LAST_INSERT细燎、PAGE_DIRECTION两曼、PAGE_N_DIRECTION主要使用來做頁分裂操作的


數(shù)據(jù)頁中行記錄存儲的排列方式

三、偽記錄分析Infimum + Supremum Record (26字節(jié)-兩個虛擬行記錄)

innodb存儲引擎有兩個偽記錄玻驻,用來界定行記錄的邊界
數(shù)據(jù)從 0x1805E 到 0x18077


  • 01 00 02 00 1E -> recorder header (5字節(jié))
  • 69 6E 66 69 6D 75 6D 00 -> 只有一個列的偽記錄悼凑,記錄內(nèi)容就是Infimum(多了一個 0x00 字節(jié)) (8字節(jié))
  • 08 00 0B 00 00 -> recorder header (5字節(jié))
  • 73 75 70 72 65 6D 75 6D -> 只有一個列的偽記錄偿枕,記錄內(nèi)容就是Supremum (8字節(jié))
分析下偽記錄中的recorder header中的next_record

recorder header最后兩個字節(jié) 0x001E,表示下一個記錄位置的偏移量,即當前行記錄“內(nèi)容”的位置0x18063 + 0x001E户辫,即0x18081渐夸,這個位置就是存放第一條實際用戶記錄


四、分析User Record

當前 Row Format 為Compact格式 可通過命令show table status like 'table_name' 進行查看

CREATE TABLE `test` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `t1` varchar(10) DEFAULT NULL,
  `t2` varchar(15) DEFAULT NULL,
  `t3` int(11) DEFAULT NULL,
  `t4` varchar(1500) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=200 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;

創(chuàng)建數(shù)據(jù)的腳本
CREATE DEFINER=`root`@`localhost` PROCEDURE `insert_test`( )
BEGIN
  #Routine body goes here...

declare i int;
declare tmp int;
set i=1;
set tmp = 1;
while i<200 do
    if tmp=1 then
        insert into test (t1, t4) values('a', REPEAT('a', i)); 
        set tmp = 0;
    else
        insert into test (t1, t3, t4) values('a', i, REPEAT('a', i)); 
        set tmp = 1;
    end if;
    set i=i+1;
end while;
END

由上面?zhèn)斡涗汭nfimum的Record Header可知下一條記錄的開始地址是0x18081,順便把前面的extra info也分析下,截圖如下


  • AD 80 01 -> 變長字段長度列表渔欢, 逆序,第一列是1字節(jié)墓塌,第四列是2字節(jié),所以第一列包含1個字符奥额,第四列包含173個 字符80 AD存的是補碼苫幢,換算成原碼為0x00AD,轉(zhuǎn)換成10進制就是173
  • 02 -> 二進制(00000010)表示第二個字段為null


  • 00 00 10 00 CC -> Record Header 固定5字節(jié)長度
  • 00 00 00 AD -> 由于是自動創(chuàng)建的int自增id 垫挨,固是4個字節(jié)韩肝,當前行記錄id為173, 由于該id是無符號的,所以最高位不是符號位
  • 00 00 00 00 69 E7 -> TransactionId
  • D7 00 00 01 5C 01 10 -> Roll Pointer
  • 61 -> 第一列字段數(shù)據(jù) a
  • 80 00 00 AD -> 第二列,存的是補碼九榔,因此原碼為0xAD哀峻,故值為173
  • 第三列為null,不占用空間
  • 61 .... 61 -> 第四列字段數(shù)據(jù) a ... a 173個 省略

分析User Record中Record Header中的內(nèi)容

0x 00 00 10 00 CC 轉(zhuǎn)換成十進制如下 00000000 00000000 00010000 00000000 11001100
下面都是二進制的哲泊,其他的都是十六進制的

  • 0 -> 預(yù)留位1
  • 0 -> 預(yù)留位2
  • 0 -> delete_mask 標記該記錄是否刪除剩蟀,0表示沒有刪除 說明刪除的數(shù)據(jù)很可能還在頁中,并且占用著空間
  • 0 -> min_rec_mask 標記該記錄是否為B+樹的非葉子節(jié)點中的最小記錄
  • 0000 -> n_owned 表示當前槽管理的記錄數(shù)
  • 00000000 00010 -> heap_no 表示當前記錄在記錄堆的位置信息切威,這個值表示當前記錄在heap中的位置為2
  • 000 -> record_type 表示當前記錄的類型育特,0表示普通記錄
  • 00000000 11001100 -> next_record 表示下一條記錄的相對位置,轉(zhuǎn)換16進制為0xCC,0x18081 + 0xCC = 0x1814D牢屋,下一條記錄的值地址為0x1814D且预,截圖如下


簡單用圖可表示如下(忽略實際內(nèi)容):


五槽袄、分析Page Directory

這一頁的末尾是0x1BFFF烙无,并且加上Page Header中PAGE_N_DIR_SLOTS,能夠知道Page Directory中包含了7個slot 截圖如下


位置是從 0x1BFEA - 0x1BFF7,一共14個字節(jié),因此展開如下:

  • 00 70 -> supremum記錄所在行偏移量地址
  • 10 2C -> id為192的行偏移量地址
  • 0C C2 -> id為188的行偏移量地址
  • 09 68 -> id為184的行偏移量地址
  • 06 1E -> id為180的行偏移量地址
  • 02 E4 -> id為176的行偏移量地址
  • 00 63 -> infimum記錄所在行偏移量地址

六遍尺、分析File Tailer

固定占用8個字節(jié)截酷,并且是在頁尾部,可以直接得出位置為0x1BFF8 開始的

  • 72 08 C8 7F -> Old-style Checksum
  • 00 38 23 77 -> Low 32 bit of LSN

為了保證頁能夠完整地寫入磁盤(如可能發(fā)生的寫入過程中磁盤損壞乾戏、機器宕機等原因)迂苛,InnoDB存儲引擎的頁中設(shè)置了File Trailer部分。File Trailer只有一個FIL_PAGE_END_LSN部分鼓择,占用8個字節(jié)三幻。前4個字節(jié)代表該頁的checksum值,最后4個字節(jié)和File Header中的FIL_PAGE_LSN相同呐能。通過這兩個值來和File Header中的FIL_PAGE_SPACE_OR_CHKSUM和FIL_PAGE_LSN值進行比較念搬,看是否一致(checksum的比較需要通過InnoDB的checksum函數(shù)來進行比較抑堡,不是簡單的等值比較),以此來保證頁的完整性(not corrupted)朗徊。

數(shù)據(jù)頁格式

File Header

名稱 大惺籽(字節(jié)) 說明
FIL_PAGE_SPACE_OR_CHKSUM 4 當mysql為4.0.14之前的版本時,該值為0爷恳。在之后的mysql版本中有缆,該值代表頁的checksum值(一種新的checksum值)
Fil_PAGE_OFFSET 4 表空間中頁的偏移值,如果獨立表空間a.ibd的大小為1GB温亲,如果頁的大小為16kb棚壁,那么總共有65536個頁.FIL_PAGE_OFFSET表示該頁在所有頁中的位置。若此表空間的ID為10栈虚,那么搜索頁(10, 1)就表示在表a中的第二頁
FIL_PAGE_PREV 4 當前頁的上一頁灌曙,B+樹的特性就決定了葉子節(jié)點必須是雙向列表
FIL_PAGE_NEXT 4 當前頁的下一頁
FIL_PAGE_LSN 8 該值代表頁最后被修改的日志序列位置LSN
FIL_PAGE_TYPE 2 INNODB 存儲頁的類型,
FIL_PAGE_FILE_FLUSH_LSN 8 該值僅在系統(tǒng)表空間的一個頁中定義节芥,代表文件至少更新到了LSN值在刺。對于獨立表空間,該值為0
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID 4 從mysql4.1開始头镊,該值代表屬于哪個表空間

Innodb存儲引用中頁的類型

名稱 十六進制 解釋
FIL_PAGE_INDEX 0x45BF B+樹葉節(jié)點
FIL_PAGE_UNDO_LOG 0x0002 undo log頁
FIL_PAGE_INODE 0x0003 索引節(jié)點
FIL_PAGE_IBUF_FREE_LIST 0x0004 insert buffer空閑列表
FIL_PAGE_TYPE_ALLOCATED 0x0000 該頁為最新分配頁
FIL_PAGE_IBUF_BITMAP 0x0005 insert buffer 位圖
FIL_PAGE_TYPE_SYS 0x0006 系統(tǒng)頁
FIL_PAGE_TYPE_TRX_SYS 0x0007 事務(wù)系統(tǒng)數(shù)據(jù)
FIL_PAGE_TYPE_FSP_HDR 0x0008 File space Header
FIL_PAGE_TYPE_XDES 0x0009 擴展描述頁
FIL_PAGE_TYPE_BLOB 0x000A BLOB頁

Page Header

用來數(shù)據(jù)頁的狀態(tài)信息,14部分組成蚣驼,共56字節(jié)

名稱 大小(字節(jié)) 說明
PAGE_N_DIR_SLOTS 2 在Page Directory (頁目錄〉中 的Slot (槽〉 數(shù)相艇,“4.4.S Page Directory” 小節(jié)中會介紹
PAGE HEAP TOP 2 堆中第一個記錄的指針颖杏, 記錄在頁中是根據(jù)堆 的形式存放的 堆中空閑空間的位置(偏移量)
PAGE N HEAP 2 堆中的記錄數(shù). 一共占用2 字節(jié), 但是第15 位表示行記錄格式 (包括最小和最大記錄以及標記為刪除的記錄)
PAGE FREE 2 指向可重用空間的首指針 指向頁中空閑空間的位置(偏移量)(就是標記為刪除的記錄地址)
PAGE GARBAGE 2 己刪除記錄的字節(jié) 數(shù)坛芽, 即行記錄結(jié)構(gòu)中也陽也在為1的記錄大小的總數(shù)
PAGE LAST INSERT 2 最后捕入記錄的位置(偏移量)
PAGE DIRECTION 2 最后插入的方向. 可能的取值為2 留储。 1.PAGE LEFT (0x01) 2.PAGE RIGHT (Ox02) 3.PAGE SAME REC (Ox03) 4. PAGE SAME PAGE (Ox04)
PAGE N DIRECTION 2 一個方向連續(xù)插入記錄的數(shù)量
PAGE N RECS 2 該頁中記錄的數(shù)量
AGE MAX TRX ID 8 修改當前頁 的最大事務(wù)ID, 注意該值僅在Secondary Index中定義
PAGE LEVEL 2 當前頁 在索引樹中的位置咙轩, OxOO代表葉節(jié)點获讳, l!P時節(jié) J點總是在第0層
PAGE INDEX ID 8 索引ID, 表示當前頁屬于哪個索引
PAGE BTR SEG LEAF 10 B+樹數(shù)據(jù)頁非頁節(jié)點所在段的segment header活喊。 注意該值僅在 B+ 樹的 Root 頁中定義
PAGE BTR SEG TOP 10 B+樹數(shù)據(jù)頁所在段的 segment header. 注意該值僅在 B+樹的 Root 頁中定義

COMPACT行記錄格式

COMPACT行記錄格式
名稱 大胸はァ(bit) 描述
預(yù)留位1 1 沒有使用
預(yù)留位2 1 沒有使用
delete_mask 1 標記該記錄是否被刪除
min_rec_mask 1 標記該記錄是否為B+樹的非葉子節(jié)點中的最小記錄
n_owned 4 表示當前槽管理的記錄數(shù)
heap_no 13 表示當前記錄在記錄堆的位置信息
record_type 3 表示當前記錄的類型,0表示普通記錄钾菊,1表示B+樹非葉節(jié)點記錄帅矗,2表示最小記錄,3表示最大記錄
next_record 16 表示下一條記錄的相對位置
Total 40(Byte) nothing

參考借鑒:

InnoDB數(shù)據(jù)頁結(jié)構(gòu)
MYSQL內(nèi)核:INNODB存儲引擎 卷1-4
MySQL技術(shù)內(nèi)幕InnoDB存儲引擎第2版

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末煞烫,一起剝皮案震驚了整個濱河市浑此,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌滞详,老刑警劉巖凛俱,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件喘落,死亡現(xiàn)場離奇詭異,居然都是意外死亡最冰,警方通過查閱死者的電腦和手機瘦棋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來暖哨,“玉大人赌朋,你說我怎么就攤上這事∑茫” “怎么了沛慢?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長达布。 經(jīng)常有香客問我团甲,道長,這世上最難降的妖魔是什么黍聂? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任躺苦,我火速辦了婚禮,結(jié)果婚禮上产还,老公的妹妹穿的比我還像新娘匹厘。我一直安慰自己,他們只是感情好脐区,可當我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布愈诚。 她就那樣靜靜地躺著,像睡著了一般牛隅。 火紅的嫁衣襯著肌膚如雪炕柔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天媒佣,我揣著相機與錄音匕累,去河邊找鬼。 笑死丈攒,一個胖子當著我的面吹牛哩罪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播巡验,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼碘耳!你這毒婦竟也來了显设?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤辛辨,失蹤者是張志新(化名)和其女友劉穎捕捂,沒想到半個月后瑟枫,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡指攒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年慷妙,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片允悦。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡膝擂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出隙弛,到底是詐尸還是另有隱情架馋,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布全闷,位于F島的核電站叉寂,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏总珠。R本人自食惡果不足惜屏鳍,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望局服。 院中可真熱鬧孕蝉,春花似錦、人聲如沸腌逢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽搏讶。三九已至佳鳖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間媒惕,已是汗流浹背系吩。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留妒蔚,地道東北人穿挨。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像肴盏,于是被迫代替她去往敵國和親科盛。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,779評論 2 354

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