InnoDB的行記錄格式, Compact, Redundant, Compressed, Dynamic

InnoDB存儲引擎和大多數數據庫同樣(如Oracle和Microsoft SQL Server數據庫),記錄是以行的形式存儲的。這意味着頁中保存着表中一行行的數據。到MySQL 5.1時,InnoDB存儲引擎提供了CompactRedundant兩種格式來存放行記錄數據,Redundant是爲兼容以前版本而保留的,若是你閱讀過InnoDB的源代碼,會發現源代碼中是用PHYSICAL RECORD(NEW STYLE)和PHYSICAL RECORD(OLD STYLE)來區分兩種格式的。MySQL 5.1默認保存爲Compact行格式。你能夠經過命令SHOW TABLE STATUS LIKE 'table_name'來查看當前表使用的行格式,其中row_format就表明了當前使用的行記錄結構類型。例如:mysql

use mysql;算法

show table status like 'user'\Gsql

數據庫實例的一個做用就是讀取頁中存放的行記錄。若是咱們知道規則,那麼也能夠讀取其中的記錄,如以前的py_innodb_page_info工具。下面將具體分析各格式存放數據的規則。數據庫

Compact行記錄格式

Compact行記錄是在MySQL 5.0時被引入的,其設計目標是能高效存放數據。簡單來講,若是一個頁中存放的行數據越多,其性能就越高。Compact行記錄以以下方式進行存儲: c#

Compact行格式的首部是一個非NULL變長字段長度列表,並且是按照列的順序逆序放置的。當列的長度小於255字節,用1字節表示,若大於255個字節,用2個字節表示,變長字段的長度最大不能夠超過2個字節(這也很好地解釋了爲何MySQL中varchar的最大長度爲65 535,由於2個字節爲16位,即216=1=65 535)。第二個部分是NULL標誌位,該位指示了該行數據中是否有NULL值,用1表示。該部分所佔的字節應該爲bytes。接下去的部分是爲記錄頭信息(record header),固定佔用5個字節(40位),每位的含義見下表4-1。最後的部分就是實際存儲的每一個列的數據了,須要特別注意的是,NULL不佔該部分任何數據,即NULL除了佔有NULL標誌位,實際存儲不佔有任何空間。另外有一點須要注意的是每行數據除了用戶定義的列外,還有兩個隱藏列,事務ID列和回滾指針列,分別爲6個字節和7個字節的大小。若InnoDB表沒有定義Primary Key,每行還會增長一個6字節的RowID列。工具

下面用一個具體事例來分析Compact行記錄的內部結構:性能

create table mytest (spa

  t1 varchar(10),設計

  t2 varchar(10),指針

  t3 char(10),

  t4 varchar(10)

) engine=innodb charset=latin1 row_format=compact;

insert into mytest values('a','bb','bb','ccc'); 

insert into mytest values('d','ee','ee','fff'); 

insert into mytest values('d',NULL,NULL,'fff'); 

select * from mytest\G; 

建立了mytest表,有4個列,t一、t二、t4都爲varchar變長字段類型,t3爲固定長度類型char。接着咱們插入了3條有表明性的數據,接着打開mytest.ibd(啓用了innodb_file_per_table,若你沒有啓用該選項,請打開默認的共享表空間文件ibdata1)。在Windows下,能夠選擇用UltraEdit打開該二進制文件(在Linux環境下,使用hexdump -C -v mytest.ibd>mytest.txt便可),打開mytest.txt文件,找到以下內容: 

0000c070 73 75 70 72 65 6d 75 6d 03 02 01 00 00 00 10 00|supremum……
0000c080 2c 00 00 00 2b 68 00 00 00 00 00 06 05 80 00 00|,……+h……
0000c090 00 32 01 10 61 62 62 62 62 20 20 20 20 20 20 20|.2..abbbb
0000c0a0 20 63 63 63 03 02 01 00 00 00 18 00 2b 00 00 00|ccc……+……
0000c0b0 2b 68 01 00 00 00 00 06 06 80 00 00 00 32 01 10|+h……2..
0000c0c0 64 65 65 65 65 20 20 20 20 20 20 20 20 66 66 66|deeeefff
0000c0d0 03 01 06 00 00 20 ff 98 00 00 00 2b 68 02 00 00|……+h……
0000c0e0 00 00 06 07 80 00 00 00 32 01 10 64 66 66 66 00|……2..dfff.
該行記錄從0000c078開始,若整理以下,相信你會有更好的理解:
03 02 01/*變長字段長度列表,逆序*/
00/*NULL標誌位,第一行沒有NULL值*/
00 00 10 00 2c/*記錄頭信息,固定5字節長度*/
00 00 00 2b 68 00/*RowID咱們建的表沒有主鍵,所以會有RowID*/
00 00 00 00 06 05/*TransactionID*/
80 00 00 00 32 01 10/*Roll Pointer*/
61/*列1數據'a'*/
62 62/*列2'bb'*/
62 62 20 20 20 20 20 20 20 20/*列3數據'bb'*/
63 63 63/*列4數據'ccc'*/

如今第一行數據就展示在咱們眼前了。須要注意的是,變長字段長度列表是逆序存放的,03 02 01,而不是01 02 03。還須要注意的是InnoDB每行有隱藏列。同時能夠看到,固定長度char字段在未填充滿其長度時,會用0x20來進行填充。再來分析一下,記錄頭信息的最後4個字節表明next_recorder,0x6800表明下一個記錄的偏移量,當前記錄的位置+0x6800就是下一條記錄的起始位置。因此InnoDB存儲引擎在頁內部是經過一種鏈表的結構來串聯各個行記錄的

第二行我將不作整理,除了RowID不一樣外,它和第一行大同小異,有興趣的讀者能夠用上面的方法本身試試。

如今咱們關注有NULL值的第三行:

03 01/*變長字段長度列表,逆序*/

06/*NULL標誌位,第三行有NULL值*/

00 00 20 ff 98/*記錄頭信息*/

00 00 00 2b 68 02/*RowID*/

00 00 00 00 06 07/*TransactionID*/

80 00 00 00 32 01 10/*Roll Pointer*/

64/*列1數據'd'*/

66 66 66/*列4數據'fff'*/

第三行有NULL值,所以NULL標誌位再也不是00而是06了,轉換成二進制爲00000110,爲1的值即表明了第2列和第3列的數據爲NULL,在其後存儲列數據的部分,咱們會發現沒有存儲NULL,只存儲了第1列和第4列非NULL的值。這個例子很好地說明了:不論是char仍是varchar類型,NULL值是不佔用存儲空間的。

Redundant行記錄格式

Redundant是MySQL 5.0版本以前InnoDB的行記錄存儲方式,MySQL 5.0支持Redundant是爲了向前兼容性。Redundant行記錄以以下方式存儲:

從上圖能夠看到,不一樣於Compact行記錄格式,Redundant行格式的首部是一個字段長度偏移列表,一樣是按照列的順序逆序放置的。當列的長度小於255字節,用1字節表示;若大於255個字節,用2個字節表示。第二個部分爲記錄頭信息(record header),不一樣於Compact行格式,Redundant行格式固定佔用6個字節(48位),每位的含義見表4-2。從表中能夠看到,n_fields值表明一行中列的數量,佔用10位,這也很好地解釋了爲何MySQL一個行支持最多的列爲1023。另外一個須要注意的值爲1byte_offs_flags,該值定義了偏移列表佔用1個字節仍是2個字節。最後的部分就是實際存儲的每一個列的數據了。

建立一張和mytest內容徹底同樣、但行格式爲Redundant的表mytest2

create table mytest2 engine=innodb row_format=redundant as (select * from mytest);

show table status like 'mytest2'\G

select * from mytest2\G

如今row_format變爲Redundant。一樣,經過hexdump將表空間mytest2.ibd導出到文本文件mytest2.txt。打開文件,找到相似以下行:

0000c070 08 03 00 00 73 75 70 72 65 6d 75 6d 00 23 20 16|……supremum.#.
0000c080 14 13 0c 06 00 00 10 0f 00 ba 00 00 00 2b 68 0b|……+h.
0000c090 00 00 00 00 06 53 80 00 00 00 32 01 10 61 62 62|……S……2..abb
0000c0a0 62 62 20 20 20 20 20 20 20 20 63 63 63 23 20 16|bb ccc#.
0000c0b0 14 13 0c 06 00 00 18 0f 00 ea 00 00 00 2b 68 0c|……+h.
0000c0c0 00 00 00 00 06 53 80 00 00 00 32 01 1e 64 65 65|……S……2..dee
0000c0d0 65 65 20 20 20 20 20 20 20 20 66 66 66 21 9e 94|ee fff!..
0000c0e0 14 13 0c 06 00 00 20 0f 00 74 00 00 00 2b 68 0d|……t……+h.
0000c0f0 00 00 00 00 06 53 80 00 00 00 32 01 2c 64 00 00|……S……2.,d..
0000c100 00 00 00 00 00 00 00 00 66 66 66 00 00 00 00 00|……fff……

整理能夠獲得以下內容:
23 20 16 14 13 0c 06/*長度偏移列表,逆序*/
00 00 10 0f 00 ba/*記錄頭信息,固定6個字節*/
00 00 00 2b 68 0b/*RowID*/
00 00 00 00 06 53/*TransactionID*/
80 00 00 00 32 01 10/*Roll Point*/
61/*列1數據'a'*/
62 62/*列2數據'bb'*/
62 62 20 20 20 20 20 20 20 20/*列3數據'bb'Char類型*/
63 63 63/*列4數據'ccc'*/

23 20 16 14 13 0c 06,逆轉爲06,0c,13,14,16,20,23。分別表明第一列長度6,第二列長度6(6+6=0x0C),第三列長度爲7(6+6+7=0x13),第四列長度1(6+6+7+1=0x14),第五列長度2(6+6+7+1+2=0x16),第六列長度10(6+6+7+1+2+10=0x20),第七列長度3(6+6+7+1+2+10+3=0x23)。

記錄頭信息中應該注意48位中22~32位,爲0000000111,表示表共有7個列(包含了隱藏的3列),接下去的33位爲1,表明偏移列表爲一個字節。

後面的信息就是實際每行存放的數據了,這與Compact行格式大體相同。

請注意是大體相同,由於若是咱們來看第三行,會發現對於NULL的處理二者是不一樣的。

21 9e 94 14 13 0c 06/*長度偏移列表,逆序*/

00 00 20 0f 00 74/*記錄頭信息,固定6個字節*/

00 00 00 2b 68 0d/*RowID*/

00 00 00 00 06 53/*TransactionID*/

80 00 00 00 32 01 10/*Roll Point*/

64/*列1數據'a'*/

00 00 00 00 00 00 00 00 00 00/*列3數據NULL*/

66 66 66/*列4數據'fff'*/

這裏與以前Compact行格式有着很大的不一樣了,首先來看長度偏移列表,咱們逆序排列後獲得06 0c 13 14 94 9e 21,前4個值都很好理解,第5個NULL變爲了94,接着第6個列char類型的NULL值爲9e(94+10=0x9e),以後的21表明14+3=0x21。能夠看到對於varchar的NULL值,Redundant行格式一樣不佔用任何存儲空間,於是char類型的NULL值須要佔用空間。

當前表mytest2的字符集爲Latin1,每一個字符最多隻佔用1個字節。若這裏將表mytest2的字符集轉換爲utf8,第三列char固定長度類型就再也不是隻佔用10個字節了,而是10×3=30個字節,Redundant行格式下char固定字符類型將會佔據可能存放的最大值字節數。

Compressed與Dynamic行記錄格式

InnoDB Plugin引入了新的文件格式(file format,能夠理解爲新的頁格式),對於之前支持的Compact和Redundant格式將其稱爲Antelope文件格式,新的文件格式稱爲Barracuda。Barracuda文件格式下擁有兩種新的行記錄格式Compressed和Dynamic兩種。新的兩種格式對於存放BLOB的數據採用了徹底的行溢出的方式,在數據頁中只存放20個字節的指針,實際的數據都存放在BLOB Page中,而以前的Compact和Redundant兩種格式會存放768個前綴字節。

下圖是Barracuda文件格式的溢出行:

Compressed行記錄格式的另外一個功能就是,存儲在其中的行數據會以zlib的算法進行壓縮,所以對於BLOB、TEXT、VARCHAR這類大長度類型的數據能進行很是有效的存儲。

相關文章
相關標籤/搜索