MySQL-InnoDB表


作者:劉仁鵬
參考資料:《MySQL技術(shù)內(nèi)幕 InnoDB存儲(chǔ)引擎》


1.索引組織表

  • 在InnoDB中,表都是根據(jù)主鍵順序組織存放的,這種存儲(chǔ)方式的表成為索引組織表
  • 在InnoDB中,每個(gè)表都有主鍵。如果在創(chuàng)建表時(shí)沒有顯式地定義主鍵,則InnoDB會(huì)按如下方式選擇或創(chuàng)建主鍵:
  1. 判斷表中是否有非空的唯一索引,如果有,則該列即為主鍵。如果有多個(gè),選用第一個(gè)被聲明的主鍵。
  2. 如果不符合上述條件,InnoDB會(huì)自動(dòng)創(chuàng)建一個(gè)6字節(jié)大小的指針作為主鍵。

2.InnoDB邏輯存儲(chǔ)結(jié)構(gòu)

  • InnoDB的邏輯存儲(chǔ)結(jié)構(gòu)為:表空間->段->區(qū)->頁->行。頁有時(shí)也被成為塊,如下圖所示:
    InnoDB邏輯存儲(chǔ)結(jié)構(gòu).png-274.4kB
    InnoDB邏輯存儲(chǔ)結(jié)構(gòu).png-274.4kB

1.表空間

  • 如果啟用了innodb_file_per_table參數(shù),需要注意的是每張表的表空間內(nèi)存放的只是數(shù)據(jù)、索引和插入緩沖Bitmap頁,其他類的數(shù)據(jù),如回滾信息,插入緩沖索引頁、系統(tǒng)事務(wù)信息,二次寫緩沖等還是存放在原來的共享表空間。

2.段

  • 常見的段有數(shù)據(jù)段、索引段、回滾段等。
  • 因?yàn)镮nnoDB是索引組織的,因此數(shù)據(jù)即索引,索引即數(shù)據(jù)。數(shù)據(jù)段即為B+樹的葉子節(jié)點(diǎn),索引段即為B+樹的非葉子節(jié)點(diǎn)?;貪L段較為特殊,后續(xù)單獨(dú)介紹。

3.區(qū)

  • 區(qū)是由連續(xù)頁組成的空間,在任何情況下每個(gè)區(qū)的大小都為1MB。為保證區(qū)中頁的連續(xù)性,InnoDB一次從磁盤申請(qǐng)4~5個(gè)區(qū)。默認(rèn)情況下,InnoDB頁大小為16K,即一個(gè)區(qū)中共有64個(gè)連續(xù)頁。
  • 通過參數(shù)KEY_BLOCK_SIZE來使用壓縮頁,或通過innodb_page_size 改變頁大小,都會(huì)導(dǎo)致一個(gè)區(qū)中頁的數(shù)量發(fā)生變化。但無論頁大小怎么變,區(qū)的大小總是1M。
  • 但還存在這樣一個(gè)問題:在用戶啟用innodb_file_per_table后,創(chuàng)建的表默認(rèn)大小是96KB。但區(qū)中是64個(gè)連續(xù)的頁,所以創(chuàng)建的表的大小至少是1MB才對(duì)???其實(shí)這是因?yàn)樵?strong>每個(gè)段開始時(shí),先用32個(gè)頁大小的碎片頁來存放數(shù)據(jù),在使用完這些頁后才是64個(gè)連續(xù)頁的申請(qǐng)。這樣做的目的是,對(duì)于一些小表,或是undo這類的段,可以在開始時(shí)申請(qǐng)較少的空間,節(jié)省磁盤容量。

4.頁

  • 頁是InnoDB磁盤管理的最小單位。
  • 從InnoDB1.2.x版本開始,可通過參數(shù)innodb_page_size將頁的大小設(shè)置為4K、8K、16K。若設(shè)置完成,則所有表中頁的大小都為innodb_page_size,不可以對(duì)其再次修改。除非通過mysqldump導(dǎo)入和導(dǎo)出來產(chǎn)生新的庫。
  • InnoDB中,常見的頁類型有:
  1. 數(shù)據(jù)頁
  2. undo頁
  3. 系統(tǒng)頁
  4. 事務(wù)數(shù)據(jù)頁
  5. 插入緩沖位圖頁
  6. 插入緩沖空閑列表頁
  7. 未壓縮的二進(jìn)制大對(duì)象頁
  8. 壓縮的二進(jìn)制大對(duì)象頁

5.行

  • InnoDB是面向行(row-oriented)的,也就是說數(shù)據(jù)是按行進(jìn)行存放的。每個(gè)頁存放的行記錄是有硬性規(guī)定的:最多允許存放 16KB/2-200 行的記錄,即7992行記錄。

3.InnoDB行記錄格式

  • InnoDB提供了CompactRedundant兩種格式來存放行記錄數(shù)據(jù)。其中Redundant是為兼容老版本而保留,建議使用Compact格式。

1.Compact行記錄格式

  • Compact在MySQL5.0中引入,其設(shè)計(jì)目標(biāo)是高效地存儲(chǔ)數(shù)據(jù)。
  • Compact行記錄存儲(chǔ)方式:


    Compact行記錄格式.png-101.3kB
    Compact行記錄格式.png-101.3kB
  1. 第一部分是一個(gè)非NULL變長字段長度列表,且其是按列的順序逆序放置的。
  2. 第二部分是NULL標(biāo)志位,該位指示了改行數(shù)據(jù)中是否有NULL值。
  3. 第三部分是記錄頭信息,固定占用5字節(jié)(40位),每位含義如下:


    Compact記錄頭信息.png-196.5kB
    Compact記錄頭信息.png-196.5kB
  4. 最后的部分是實(shí)際存儲(chǔ)每個(gè)列的數(shù)據(jù)。
  • 需要注意的是:
  1. NULL除了占有NULL標(biāo)志位,實(shí)際存儲(chǔ)不占任何空間。
  2. 每行數(shù)據(jù)除了用戶定義的列之外,還有兩個(gè)隱藏列,事務(wù)ID列回滾指針列。分別為6字節(jié)和7字節(jié)的大小。若InnoDB表沒有定義主鍵,每行還會(huì)增加一個(gè)6字節(jié)的rowid列。
  3. 固定長度CHAR字段在未能完全占用其長度空間時(shí),會(huì)用0x20來進(jìn)行填充。
  4. 記錄頭信息的最后兩個(gè)字節(jié)代表next_recorder,代表下一條記錄的偏移量,所以InnoDB在頁內(nèi)部是通過一種鏈表的結(jié)構(gòu)來串連各個(gè)行記錄的。

2.Redundant行記錄格式

  • Redundant是MySQL5.0版本之前InnoDB的行記錄格式,其存在是為了兼容老版本的頁格式。
  • Redundant行記錄存儲(chǔ)方式:


    Redundant行記錄格式.png-178.3kB
    Redundant行記錄格式.png-178.3kB
    1. 第一部分是一個(gè)字段長度偏移列表,同樣是按列的順序逆序放置的。
    2. 第二部分是記錄頭信息,不同于Compact,Redundant占用6字節(jié)(48位),每位含義如下:


      Redundant記錄頭信息.png-198.9kB
      Redundant記錄頭信息.png-198.9kB
    • 其中n_fields值代表一行中列的數(shù)量,占用10位。這也解釋了為什么MySQL 一行支持的最多列數(shù)為1023。
  • 需要注意的是:
  1. 對(duì)于NULL值的處理,Redundant和Compact非常不同:對(duì)于VARCHAR類型的NULL值,Redundant同樣不占用任何空間,但CHAR類型的NULL值需要占用最大值字節(jié)數(shù)大小的空間。

3.行溢出數(shù)據(jù)

  • InnoDB可以將一條記錄中的某些數(shù)據(jù)存儲(chǔ)在真正的數(shù)據(jù)頁面之外。
  • 是否溢出與列類型是否為BLOB等大對(duì)象列類型并無直接關(guān)系。而是根據(jù)“保證一個(gè)頁至少能存放兩條記錄”的標(biāo)準(zhǔn)來判斷的。如果VERCHAR類型的列長度過長導(dǎo)致一頁只能存儲(chǔ)一條記錄,則也會(huì)被放到Uncompressed BLOB Page(行溢出數(shù)據(jù)頁)。之所以有這個(gè)標(biāo)準(zhǔn)的原因,是因?yàn)槿绻荒鼙WC如此,那B+Tree就是去意義變成鏈表了。
  • InnoDB能存放VARCHAR類型的最大長度為65532字節(jié) (注意并非65535,這其中還有其他開銷)。另外要注意,VARCHAR(N)中的N指的是字符的長度而非字節(jié)。另外,MySQL手冊中定義的65535字節(jié)長度是指所有VARCHAR列的長度總和。
  • 當(dāng)發(fā)生行溢出時(shí),數(shù)據(jù)頁中值保存了列的前768字節(jié)的前綴數(shù)據(jù),之后是偏移量,指向行溢出頁。如下圖所示:
    行溢出數(shù)據(jù)的存儲(chǔ).png-27.2kB
    行溢出數(shù)據(jù)的存儲(chǔ).png-27.2kB

4.Compressed和Dynamic行記錄格式

  • 從InnoDB1.0.x開始引入了新的文件格式(可理解為頁格式):Barracuda。而之前的文件格式被稱為Antelope,Barracuda包含了Antelope:


    文件格式.png-59kB
    文件格式.png-59kB
  • 新的兩種行記錄格式對(duì)于存放在BLOB中的數(shù)據(jù)采用了完全的行溢出方式,在數(shù)據(jù)頁中只存放20個(gè)字節(jié)的指針,實(shí)際的數(shù)據(jù)都存放子啊OffPage中。而之前的兩種行記錄格式都是會(huì)存放768個(gè)前綴字節(jié)。新的行溢出方式如下:


    B行溢出方式.png-23.5kB
    B行溢出方式.png-23.5kB
  • Compressed行記錄格式的另一個(gè)功能就是:存儲(chǔ)在其中的行數(shù)據(jù)會(huì)以zlib的算法進(jìn)行壓縮。因此對(duì)于BLOB、TEXT、VARXCHAR這些大長度類型的數(shù)據(jù)能夠非常有效的存儲(chǔ)。

5.CHAR的行存儲(chǔ)結(jié)構(gòu)

  • 從MySQL4.1開始,CHAR(N)中的N指的是字符的長度,而不是之前版本的字節(jié)長度。也就是說在不同字符集下,CHAR類型列內(nèi)部存儲(chǔ)的可能不是定長的數(shù)據(jù)。
  • 另外由于多字節(jié)的字符編碼,不同字符的長度可能不同,所以CHAR類型不再代表固定長度的字符串了。因此,對(duì)于多字節(jié)字符編碼的CHAR類型的存儲(chǔ),InnoDB在內(nèi)部將其視為變長字符類型。這也就意味著在變長長度列表中會(huì)記錄CHAR數(shù)據(jù)類型的長度。只是對(duì)于未能占滿長度的字符還是填充0x20。

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

  • 通過前面內(nèi)容我們已了解到,頁是InnoDB管理數(shù)據(jù)庫的最小磁盤單位
  • InnoDB數(shù)據(jù)頁由以下七部分組成:


    InnoDB數(shù)據(jù)頁結(jié)構(gòu).png-310.3kB
    InnoDB數(shù)據(jù)頁結(jié)構(gòu).png-310.3kB

1.File Header:文件頭

  • File Header用來記錄頁的一些頭信息,共由如下8部分組成,共占用38字節(jié):


    FileHeader.png-469.4kB
    FileHeader.png-469.4kB
  • InnoDB頁的類型:


    InnoDB頁類型.png-272.3kB
    InnoDB頁類型.png-272.3kB

2.Page Header:頁頭

  • 該部分用來記錄數(shù)據(jù)頁的狀態(tài)信息,由14個(gè)部分組成,共56字節(jié):


    PageHeader.png-479.8kB
    PageHeader.png-479.8kB

    PageHeader2.png-116.1kB
    PageHeader2.png-116.1kB

3.Infimum和Supremum Record

  • 在InnoDB中,每個(gè)數(shù)據(jù)頁都有兩個(gè)虛擬的行記錄,用來限定記錄的邊界。
  • Infimum記錄是比該頁中任何主鍵值都要小的值。
  • Supremum指比任何可能大的值還要大的值。
  • 這兩個(gè)值在頁創(chuàng)建時(shí)被建立,且在任何情況下都不會(huì)被刪除。
  • 示意圖如下:


    record.png-184.8kB
    record.png-184.8kB

4.User Record和Free Space

  • User Record,用戶記錄,即行記錄。
  • Free Space,空閑空間。是個(gè)鏈表數(shù)據(jù)結(jié)構(gòu)。在一條記錄被刪除后,該空間會(huì)被加入到空閑鏈表中。

5.Page Directory:頁目錄

  • Page Directory中存放了記錄的相對(duì)位置,有時(shí)將這些記錄指針稱為Slots()或Directory Slots(目錄槽)。
  • InnoDB中并不是每個(gè)記錄都擁有一個(gè)槽,InnoDB的槽是一個(gè)稀疏目錄,即一個(gè)槽中可能包含多個(gè)記錄。當(dāng)記錄被插入或刪除時(shí),需要對(duì)槽進(jìn)行分裂或平衡的維護(hù)操作。
  • 在槽中記錄按照索引鍵信息順序存放,這樣可以利用二叉查找迅速找到記錄的指針。
  • 需要注意的是:B+樹索引本身并不能找到具體的一條記錄,能找到只是該記錄所在的頁。數(shù)據(jù)庫把頁載入到內(nèi)存,然后通過Page Directory再進(jìn)行二叉查找。只不過二叉查找的時(shí)間復(fù)雜度很低,同時(shí)在內(nèi)存中查找很快,因此通常忽略這部分時(shí)間。

6.File Trailer:文件結(jié)尾信息

  • 為了檢測頁是否已完整地寫入磁盤(如寫入時(shí)可能發(fā)生磁盤損壞、機(jī)器關(guān)機(jī)等),InnoDB設(shè)置了File Trailer來保證頁的完整性。

end

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

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

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