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

(本片博文是《MySQL技術(shù)內(nèi)幕 InnoDB存儲引擎第二版》的讀書筆記)

索引組織表

InnoDB存儲引擎中,表都是按照主鍵順序組織存放的,這種存儲方式的表稱為索引組織表(index organized table)。InnoDB存儲引擎表中,每張表都有個(gè)主鍵,若在創(chuàng)建表時(shí)沒有顯式定義主鍵則InnoDB存儲引擎會按如下方式選擇或者創(chuàng)建主鍵:

  • 首先判斷表中是否有非空的唯一索引(Unique NOT NULL),如果有,則該列即為主鍵。(當(dāng)表中有多個(gè)非空唯一索引時(shí),InnoDB存儲引擎將選擇建表時(shí)第一個(gè)定義的非空唯一索引為主鍵,注意是根據(jù)定義索引的順序而不是建表時(shí)列的順序)。
  • 如果不符上述條件,則會自動創(chuàng)建一個(gè)6字節(jié)大小的指針。

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

這是《MySQL技術(shù)內(nèi)幕 InnoDB存儲引擎》一書中對InnoDB邏輯存儲結(jié)構(gòu)的描述。


1.PNG

表空間

表空間是Innodb存儲引擎邏輯的最高層,所有的數(shù)據(jù)都存放在表空間中,默認(rèn)情況下,Innodb存儲引擎有一個(gè)共享表空間ibdata1,即所有數(shù)據(jù)都存放在這個(gè)表空間中內(nèi)。如果啟用了innodb_file_per_table參數(shù),則每張表內(nèi)的數(shù)據(jù)可以單獨(dú)放到一個(gè)表空間內(nèi),但請注意,只有數(shù)據(jù)、索引、和插入緩沖Bitmap放入單獨(dú)表內(nèi),其他數(shù)據(jù),比如回滾(undo)信息、插入緩沖檢索頁、系統(tǒng)事物信息,二次寫緩沖等還是放在原來的共享表內(nèi)的。

從上圖中可以看出表空間由段組成,常見的段有數(shù)據(jù)段、索引段、回滾段等。因?yàn)镮nnoDB存儲引擎表是索引組織的,因此數(shù)據(jù)即索引,索引即數(shù)據(jù)。數(shù)據(jù)段即為B+樹的葉子結(jié)點(diǎn),索引段即為B+樹的非索引結(jié)點(diǎn)。在InnoDB存儲引擎中對段的管理都是由引擎自身所完成,DBA不能也沒必要對其進(jìn)行控制。

區(qū)

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

頁(塊)

頁是InnoDB磁盤管理的最小單位。在InnoDB存儲引擎中,默認(rèn)每個(gè)頁的大小為16KB。從InnoDB1.2.x版本開始,可以通過參數(shù)innodb_page_size將頁的大小設(shè)置為4K,8K,16K。若設(shè)置完成,則所有表中頁的大小都固定,不可以對其再次修改。除非通過mysqldump導(dǎo)入和導(dǎo)出操作來產(chǎn)生新的庫。
InnoDB存儲引擎中,常見的頁類型有:數(shù)據(jù)頁,undo頁,系統(tǒng)頁,事務(wù)數(shù)據(jù)頁,插入緩沖位圖頁,插入緩沖空閑列表頁等。

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

InnoDB數(shù)據(jù)頁結(jié)構(gòu)如下圖:

1.PNG

其中File Header、Page Header、File Trailer的大小是固定的,分別為38,56,8字節(jié),這些空間用來標(biāo)記該頁的一些信息,如Checksum,數(shù)據(jù)頁所在B+樹索引的層數(shù)等。User Records、Free Space、Page Directory這些部分為實(shí)際的行記錄存儲空間,因此大小是動態(tài)的。

1.File Header用來記錄頁的一些頭信息,由表中8個(gè)部分組成,共占38字節(jié)。各部分代表信息如下表所示:

1.PNG

2.PNG

2.Page Header用來記錄數(shù)據(jù)頁的狀態(tài)信息,14個(gè)部分組成,共占56字節(jié)。各部分代表信息如下表所示:

1.PNG

2.PNG

3.Infimum和Supermum Record是InnoDB中兩個(gè)虛擬的行記錄,用來限定記錄的邊界。Infimum記錄是比該頁中任何主鍵值都要小的值,Supermum是比任何可能大的值還要大的值。這兩個(gè)記錄在頁創(chuàng)建時(shí)被建立,并且在任何情況下都不會被刪除。下圖顯示了Infimum記錄和Supermum記錄:
3.PNG

4.User Record和Free Space

User Record就是實(shí)際存儲行記錄的內(nèi)容。InnoDB存儲引擎表總是B+樹索引組織的。Free Space指空閑空間,是鏈表數(shù)據(jù)結(jié)構(gòu),在一條記錄被刪除后,該空間會被加入到空閑鏈表中。
5.Page Directory

Page Directory中存放了記錄的相對位置(是頁相對位置而不是偏移量),有時(shí)這些記錄指針稱為Slots(槽)或者目錄槽(Directory Slots)。與其他數(shù)據(jù)庫系統(tǒng)不同的是,在InnoDB中并不是每個(gè)記錄擁有一個(gè)槽,InnoDB存儲引擎的槽是一個(gè)稀疏目錄(sparse directory),即一個(gè)槽中可能包含多個(gè)紀(jì)錄。偽記錄的Infimum的n_owned值總是為1,記錄Supermum的n_owned的取值范圍為[1,8],其他用戶記錄n_owned的取值范圍為[4,8]。當(dāng)記錄被插入或刪除時(shí)需要對槽進(jìn)行分裂或平衡的維護(hù)操作。

在Slots中記錄按照索引鍵值順序存放,這樣可以利用二叉查找迅速找到記錄的指針。假設(shè)有('i','d','c','b','e','g','l','h','f','j','k','a'),同時(shí)假設(shè)一個(gè)槽中包含4條記錄,則Slots中的記錄可能是('a','e','i')。

由于在InnoDB存儲引擎中Page Directory是稀疏目錄,二叉查找的結(jié)果只是一個(gè)粗略結(jié)果,因此InnoDB存儲引擎必須通過recorder header中的next_record來繼續(xù)查找相關(guān)記錄。同時(shí),Page Directory很好地解釋了recorder header中的n_owned值的含義,因?yàn)檫@些記錄并不包括在Page Directory中。

B+樹索引本身并不能找到具體的一條記錄,能找到只是改記錄所在的頁。數(shù)據(jù)庫把頁載入到內(nèi)存,然后通過Page Directory再進(jìn)行二叉查找。只不過二叉查找的時(shí)間復(fù)雜度很低,同時(shí)在內(nèi)存中的查找很快,因此通常忽略這部分查找所用的時(shí)間。

6.File Trailer
該部分是為了檢測頁是否已經(jīng)完整地寫入磁盤的(可能在寫入過程中磁盤損壞、機(jī)器關(guān)機(jī)等)。該部分占用8字節(jié),前4字節(jié)代表該頁的checksum值,后4字節(jié)和File Header中的FIL_PAGE_LSN相同。將這兩個(gè)值與File Header中的FIL_PAGE_SPACE_OR_CHKSUM和FIL_PAGE_LSN值進(jìn)行比較,看是否一致(checksum的比較需要通過InnoDB的checksum函數(shù)來比較,不是簡單的等值比較),以此來保證頁的完整新。

InnoDB存儲引擎是面向列的,也就是說數(shù)據(jù)按行存放。每個(gè)頁存放的行記錄也是有硬性定義的,最多允許存放16KB/2-200=7992行記錄。
InnoDB存儲引擎提供了Compact和Redundant兩種格式來存放行記錄數(shù)據(jù),Redundant格式是為兼容之前版本而保留的。5.1版本中,默認(rèn)設(shè)置為Compact行格式??梢酝ㄟ^命令SHOW TABLE STATUS LIKE 'table_name'來查看當(dāng)前表使用的行格式,其中row_format屬性表示行記錄類型。

Compact行記錄格式

3.PNG

上圖就是Compact行記錄的存儲方式。由圖可知,首部是一個(gè)非NULL變長字段長度列表,并且是按照列的順序逆序放置的,長度為:

  • 若列的長度小于255字節(jié),用1字節(jié)表示;
  • 若大于255字節(jié),用2字節(jié)表示。

變長字段的長度最大不可以超過2字節(jié),因?yàn)樵贛ySQL中VARCHAR類型的最大長度限制為65535。變長字段之后的第二個(gè)部分是NULL標(biāo)志位,指示了該行數(shù)據(jù)中是否有NULL值,有則用1表示,占1字節(jié)。接下來是記錄頭信息,固定占用5字節(jié)。每位的含義見下表

4.PNG

最后的部分就是實(shí)際存儲每個(gè)列的數(shù)據(jù)。NULL不占該部分任何空間,即NULL除了占有NULL標(biāo)志位,實(shí)際存儲不占有任何空間。另外有一點(diǎn)需要注意的是,每行數(shù)據(jù)除了用戶定義的列外,還有兩個(gè)隱藏列,事務(wù)ID和回滾指針列,分別為6字節(jié)和7字節(jié)的大小。若InnoDB表沒有定義主鍵,每行還會增加一個(gè)6字節(jié)的rowid列。

Redundant行記錄格式

Redundant是MySQL5.0版本之前InnoDB的行記錄存儲方式,MySQL5.0支持Redundant是為了兼容之前版本的頁格式。Redundant行記錄采用如圖所示的方式存儲。

1.PNG

首部是字段長度偏移列表,按照列的順序逆序放置。若列的長度小于255字節(jié),用1字節(jié)表示;若大于255字節(jié),用2字節(jié)表示。第二部分為記錄頭信息,占用6字節(jié),每位含義如下表
2.PNG

從表中可以看出,n_fields值代表一行中列的數(shù)量,占用10位,很好的解釋了為什么MySQL數(shù)據(jù)庫一行支持最多的列為1023。另一個(gè)需要知道的是1byte_offs_flags,該值定義了偏移列表占用1字節(jié)還是2字節(jié)。最后的部分就是實(shí)際存儲的每個(gè)列的數(shù)據(jù)了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容