InnoDB邏輯存儲結(jié)構(gòu)

簡介

image.png

從InnoDB邏輯存儲結(jié)構(gòu)來看,InnoDB所有數(shù)據(jù)都存放到在一個空間中,稱之為表空間。如圖所示,表空間由段、區(qū)、頁組成。

表空間

表空間可以看做是InnoDB存儲引擎邏輯結(jié)構(gòu)的最外層。之前的文章Mysql——InnoDB存儲引擎架構(gòu)就已經(jīng)介紹過了,表空間分為系統(tǒng)表空間、獨(dú)立表空間、常規(guī)表空間、undo獨(dú)立表空間、共享臨時表空間。

表空間是由各個段組成的,常見的段有數(shù)據(jù)段、索引段、回滾段等。

  • 數(shù)據(jù)段:B+樹的葉子節(jié)點(diǎn);
  • 索引段:B+樹的中間節(jié)點(diǎn);
  • 回滾段:記錄undo log。

區(qū)

段是由各個區(qū)組成的,而區(qū)是由多個頁組成的,在任何情況下每個區(qū)的大小都為1M。為了保證區(qū)中頁的連續(xù)性,InnoDB存儲引擎申請空間是按區(qū)為單位申請(一次從磁盤申請4~5個區(qū))。默認(rèn)的情況下,InnoDB中頁的大小=16KB,即一個區(qū)有64個連續(xù)頁。

當(dāng)然Mysql 5.7中InnoDB存儲引擎時支持頁的壓縮,每個頁的大小可能是2K、4K、8K、16K,所以每個區(qū)對應(yīng)頁的數(shù)量變成64~512之間。

考慮這樣的一個問題,在用戶啟用了參數(shù)innodb_file_per_table后,創(chuàng)建的表默認(rèn)大小為96K,上面說到每個區(qū)的是連續(xù)的64個頁,創(chuàng)建表的時候應(yīng)該是1M才對?

其實(shí)是每個段開始的時候,會先用32個頁大小的碎片頁來存放數(shù)據(jù)(也就是說創(chuàng)建表的時候默認(rèn)會用到6個碎片頁),當(dāng)著32個碎片頁用完的時候才會申請連續(xù)的64個頁。對于一些小表來說,可以再開始的時候申請較少的空間,節(jié)省磁盤空間。

頁是InnoDB磁盤管理的最小單位,默認(rèn)的情況下,InnoDB中頁的大小=16KB,數(shù)據(jù)庫一開始可以通過innodb_page_size將頁的大小設(shè)置為4K、8K、16K,但是一旦數(shù)據(jù)庫表被創(chuàng)建了,后面就不能再修改了。
常見的頁類型有:

  • 索引、數(shù)據(jù)頁(B+Tree Node);
  • undo頁(undo log Page);
  • 系統(tǒng)頁(System Page);
  • 事務(wù)數(shù)據(jù)頁(Transaction system Page);
  • 插入緩沖位圖頁(Insert Buffer Bitmap);
  • 插入緩沖空閑列表頁(Insert Buffer Free List);
  • 未壓縮的二進(jìn)制大對象頁(Uncompressed BLOB Page);
  • 壓縮的二進(jìn)制大對象頁(Compressed BLOB Page)。

索引、數(shù)據(jù)頁結(jié)構(gòu)

即InnoDB存儲引擎B+樹的節(jié)點(diǎn)。InnoDB的數(shù)據(jù)頁由以下7個部分組成:

  • File Header(文件頭):38字節(jié),用來記錄頁的頭部信息,由8個部分組成。
名稱 空間(字節(jié)) 說明
FIL_PAGE_SPACE_OR_CHKSUM 4 代表該頁的checksum值
FIL_PAGE_OFFSET 4 表空間頁的偏移值,也就是該頁在某個表空間所有頁的的位置
FIL_PAGE_PREV 4 當(dāng)前頁的上一個頁的位置
FIL_PAGE_NEXT 4 當(dāng)前頁的下一個頁的位置
FIL_PAGE_LSN 8 該頁最后一次被修改的日志序列位置LSN
FIL_PAGE_TYPE 2 頁的類型
FIL_PAGE_FILE_FLUSH_LSN 8 “文件已經(jīng)被刷新到磁盤上,至少到這個lsn”,僅在文件的第一頁有效
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID 4 頁屬于哪個表空間

頁的類型主要有以下幾種:

名稱 16進(jìn)制 說明
FIL_PAGE_TYPE_ALLOCATED 0x000 該頁為最新分配
FIL_PAGE_UNDO_LOG 0x002 Undo log頁
FIL_PAGE_INODE 0x003 索引節(jié)點(diǎn)
FIL_PAGE_IBUF_FREE_LIST 0x004 插入緩沖空閑列表
FIL_PAGE_IBUF_BITMAP 0x005 插入緩沖位圖
FIL_PAGE_TYPE_SYS 0x006 系統(tǒng)頁
FIL_PAGE_TYPE_TRX_SYS 0x007 事務(wù)系統(tǒng)數(shù)據(jù)
FIL_PAGE_TYPE_FSP_HDR 0x008 File Space Header
FIL_PAGE_TYPE_XDES 0x009 擴(kuò)展描述頁
FIL_PAGE_TYPE_BLOB 0x00A BLOB頁
FIL_PAGE_INDEX 0x45BF B+樹葉節(jié)點(diǎn)
  • Page Header(頁頭):56字節(jié),用來記錄數(shù)據(jù)頁的狀態(tài)信息,由14個部分組成。
名稱 空間(字節(jié)) 說明
PAGE_N_DIR_SLOTS 2 表示頁目錄的插槽數(shù)
PAGE_HEAP_TOP 2 堆中第一個記錄的指針,記錄在頁中是根據(jù)堆的形式存放的
PAGE_N_HEAP 2 堆中的記錄數(shù),第15位表示行記錄格式
PAGE_FREE 2 指向可重用空間的首指針
PAGE_GARBAGE 2 已刪除記錄的字節(jié)數(shù)
PAGE_LAST_INSERT 2 最后插入記錄的位置
PAGE_DIRECTION 2 最后插入的方向:
PAGE_LEFT(0x01)
PAGE_RIGHT(0x02)
PAGE_SMAE_REC(0x03)
PAGE_SMAE_PAGE(0x04)
PAGE_NO_DIRECTION(0x05)
PAGE_N_DIRECTION 2 一個方向連續(xù)插入的記錄數(shù)
PAGE_N_RECS 2 該頁的記錄數(shù)
PAGE_MAX_TRX_ID 8 修改當(dāng)前頁最大的事務(wù)ID,注意該值僅在二級索引定義
PAGE_LEVEL 2 當(dāng)前頁在索引樹的層數(shù)
PAGE_INDEX_ID 8 索引ID,代表該頁屬于哪個索引
PAGE_BTR_SEG_LEAF 10 B+樹數(shù)據(jù)頁非葉子節(jié)點(diǎn)所在段的segment header
PAGE_BTR_SEG_TOP 10 B+樹數(shù)據(jù)頁所在段的segment header
  • Infimun和Supremum Records:屬于InnoDB存儲引擎每個頁中的兩個虛擬行記錄,用來限定記錄的邊界。Infimun:下界,表示比任何主鍵值還要小的值;Supremum:上界,表示比任何主鍵值還要大的值;這兩個值在頁創(chuàng)建的時候被建立,在任何情況下都不會被刪除。
  • User Records(行記錄):用戶實(shí)際存儲的行記錄。
  • Free Space(空閑空間):頁的空閑空間。
  • Page Directory(頁目錄):存放記錄的相對位置的指針,這些指針可以稱之為Slots(槽)。在InnoDB中并不是每個記錄都擁有一個Slot,而是一個稀疏目錄,也就是說一個Slot可能包含多個記錄。在Slots中記錄是按照索引鍵值順序存放的,這樣可以利用二叉查找樹迅速查找到對應(yīng)的Slot。用戶查找數(shù)據(jù)的時候先通過B+樹找到對應(yīng)的頁,然后將頁加載到內(nèi)存,然后通過Page Directory進(jìn)行二叉查找,找到對應(yīng)的Slot,然后通過該Slot指向的記錄進(jìn)行next_record繼續(xù)查找相對應(yīng)的記錄。
image
  • File Trailer(文件結(jié)尾信息):8字節(jié),為了檢測頁是否已經(jīng)完整寫入磁盤(如可能發(fā)生寫入過程中磁盤損壞,機(jī)器關(guān)機(jī)等)。
名稱 空間(字節(jié)) 說明
FILE_PAGE_END_LSN 8 前4字節(jié)表示頁的checksum值;后4字節(jié)與File Header的FIL_PAGE_LSN相同。將這兩個值與File Header的FIL_PAGE_SPACE_OR_CHKSUM和FIL_PAGE_LSN進(jìn)行比較,確認(rèn)是否一致,以此來保證頁的完整性。

在InnoDB存儲引擎中,表都是根據(jù)主鍵順序組織存放的,即按照主鍵作為索引鍵值,因此InnoDB存儲引擎中的表實(shí)際上就是顆B+Tree,樹的節(jié)點(diǎn)存放的是索引、數(shù)據(jù)頁,其中葉子節(jié)點(diǎn)存放的是數(shù)據(jù),非葉子節(jié)點(diǎn)存放的是索引,每個葉子節(jié)點(diǎn)包含多行數(shù)據(jù)。
InnoDB存儲引擎對每個頁數(shù)據(jù)的存放是有硬性要求的,最多允許16KB/2 - 200 = 7992行數(shù)據(jù),最少需要2行數(shù)據(jù)。

行記錄格式

InnoDB存儲引擎提供了Redundant、Compact(Mysql5.1 默認(rèn))、Dynamic(Mysql5.7 默認(rèn))、Compressed四種格式來存放行記錄,可以使用show table status;查看表中的行記錄格式。

image

Compact行記錄格式

image

上圖展示了Compact行記錄格式,主要有以下幾部分:

  • 變長字段列表:存儲變長字段的長度,按照列的順序逆序來存儲;如果列的長度<255,用1個字節(jié)表示;否則使用2個字節(jié)表示(注意,VARCHAR最大長度為65535);

  • NULL標(biāo)志位:表示改行數(shù)據(jù)是否有空值,有則用1表示,該部分占用1個字節(jié);

  • 記錄頭信息:固定5個字節(jié)。存儲格式如下:

    名稱 空間(bit) 說明
    () 1 未知
    () 1 未知
    delete_flag 1 該行是否被刪除
    min_rec_flag 1 如果等于1,該記錄是預(yù)先被定義為最小的記錄
    n_owned 4 該記錄擁有的記錄數(shù)
    heap_no 13 索引堆中該條記錄的排序記錄
    record_type 3 記錄類型:
    000表示普通
    001表示B+樹節(jié)點(diǎn)指針
    010表示Infimum
    011表示Supremun
    1XX表示保留
    next_record 16 頁中下一條記錄的相對位置
  • 實(shí)際存儲每個列的數(shù)據(jù);

需要注意的是如果某個列的值為NULL,則不占任何空間(除了占有NULL標(biāo)志位,為1)。

每行數(shù)據(jù)除了用戶定義的以外,還有兩個隱藏列:事務(wù)ID列(6個字節(jié))和回滾指針列(7個字節(jié))。
如果表沒有定義主鍵,每行還會增加一個rowid列。

Redundant行記錄格式

image

Redundant是Mysql5.0版本之前的行記錄格式。上圖展示了Redundant行記錄格式,主要有以下幾部分:

  • 字段長度偏移列表:存儲所有字段長度的偏移量,按照列的順序逆序來存儲(例如:23 20 16 14 13 0c 06字段長度偏移列表,06代表第一列的長度=6,0c-06=第二列的長度,13-0c=第三列的長度,以此類推);如果列的長度<255,用1個字節(jié)表示;否則使用2個字節(jié)表示(注意,VARCHAR最大長度為65535);

  • 記錄頭信息:占用6個字節(jié),存儲格式如下:

    名稱 空間(bit) 說明
    () 1 未知
    () 1 未知
    delete_flag 1 該行是否被刪除
    min_rec_flag 1 如果等于1,該記錄是預(yù)先被定義為最小的記錄
    n_owned 4 該記錄擁有的記錄數(shù)
    heap_no 13 索引堆中該條記錄的排序記錄
    n_fileds 10 記錄中列的數(shù)量
    1byte_offs_flag 1 偏移列表為1個字節(jié)還是2個字節(jié)
    next_record 16 頁中下一條記錄的相對位置
  • 實(shí)際存儲每個列的數(shù)據(jù);

①考慮這樣的一個問題,之前說到頁的大小默認(rèn)16K(16384個字節(jié)),并且一個數(shù)據(jù)頁中至少要包含2行記錄,然而Mysql Varchar最大存儲字節(jié)65535個字節(jié)(當(dāng)然還有其他可變長大字段,TEXT、BLOB) > 頁的大小,兩者之間是否會互相矛盾?

事實(shí)上會互相矛盾,但mysql引入行溢出的機(jī)制來解決這個問題。

image

Compact和Redundant行記錄格式采用上圖存儲方式,數(shù)據(jù)頁只保留前768個字節(jié)的前綴數(shù)據(jù),之后是偏移量指針,指向行溢出頁。
②什么時候會觸發(fā)行溢出呢?
剛剛講到一個數(shù)據(jù)頁至少要包含2行記錄,因此,如果當(dāng)前頁只能存放一個記錄的時候,InnoDB存儲引擎就會自動把行數(shù)據(jù)存放到溢出頁中。
③對于TEXT或者BLOB,是不是總是存放到溢出頁中?
是否存放到溢出頁中,都要遵循上面第②點(diǎn)。
④Varchar最大存儲字節(jié)65535個字節(jié),是指單個列的長度么?
不是的,是指一行記錄的所有VARCHAR列長度的總和不能超過65535個字節(jié)。
⑤Varchar(n)或者Char(n)中的n指的是字節(jié)個數(shù)還是字符長度?
指的是字符長度。不同的字符集對列的長度會有影響,比如定義Varchar的最大長度,lantin則可以定義Varchar(65535),如果是utf-8(3個字節(jié))則只能定義Varchar(65535/3)。對于char(n)來說,不同的字符集長度是不固定的,InnoDB存儲引擎在內(nèi)部會將其視為變長字符類型。

Dynamic行記錄格式

InnoDB 1.0X版本開始引入新的文件格式,以前支持Compact和Redundant格式稱為Antelope文件格式,新的格式有:Compressed和Dynamic,稱為Baracuda文件格式。

Dynamic行格式提供與Compact行格式相同的存儲特性,但對于長可變長度列值(對于VARCHAR,VARBINARY和BLOB和TEXT類型)行溢出采用了完全的行溢出方式。如圖所示,在數(shù)據(jù)頁的某行記錄里的某個大可變長度列值只存放20個字節(jié)的指針,實(shí)際的數(shù)據(jù)都存放到外部溢出頁中。
image

Compressed行記錄格式

Compressed行格式提供與Dynamic行格式相同的存儲特性和功能,但增加了對表和索引數(shù)據(jù)壓縮的支持(采用zlib算法壓縮)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,401評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,011評論 3 413
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,263評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,543評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,323評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,874評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,968評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,095評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,605評論 1 331
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,551評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,720評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,242評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,961評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,358評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,612評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,330評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,690評論 2 370

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