具體細節(jié) 請去掘金購買《MySQL 是怎樣運行的:從根兒上理解 MySQL》
InnoDB頁簡介
- 1.innodb把數(shù)據(jù)按照頁為基本單位作為與磁盤的交互單位。
- 2.頁大小一般為16KB
InnoDB行格式
- 1.記錄是以行格式或者記錄格式存儲在磁盤
- 2.compact,redundant,dynamic和compressed等四只格式
指定行格式的語法
mysql> CREATE TABLE record_format_demo (
-> c1 VARCHAR(10),
-> c2 VARCHAR(10) NOT NULL,
-> c3 CHAR(10),
-> c4 VARCHAR(10)
-> ) CHARSET=ascii ROW_FORMAT=`COMPACT`;
Query OK, 0 rows affected (0.03 sec)
COMPACT行格式
- 1.主要是兩部分:記錄的額外信息和記錄的真實數(shù)據(jù)。
- 2.額外信息:變長字段長度列表,NULL值列表,記錄頭信息
- 3.真實數(shù)據(jù):各個列的值。
變長字段長度列表
- 1.VARCHAR(M)、VARBINARY(M)、各種TEXT類型,各種BLOB類型.
- 2.對于變長的字段,mysql存儲其真實數(shù)據(jù)內(nèi)容和占用的字節(jié)數(shù)。
- 3.Compact行格式中,所有變長字段的占用的字節(jié)長度放在開頭形成鏈表,是逆序存放(比如列a,列b,則鏈表就是ba)
- 4.如果該可變字段允許存儲的最大字節(jié)數(shù)(M×W)超過255字節(jié)并且真實存儲的字節(jié)數(shù)(L)超過127字節(jié),則使用2個字節(jié),否則使用1個字節(jié)。
- 5.該字節(jié)的第一個二進制位作為標志位:如果該字節(jié)的第一個位為0,那該字節(jié)就是一個單獨的字段長度(使用一個字節(jié)表示不大于127的二進制的第一個位都為0),如果該字節(jié)的第一個位為1,那該字節(jié)就是半個字段長度。
- 6.變長字段長度列表中只存儲值為 非NULL 的列內(nèi)容占用的長度,值為 NULL 的列的長度是不儲存的
NULL值列表
- 1.把列中的存儲NULL的都放入列表中,這樣就不需要再真實數(shù)據(jù)中占有地方
- 2.如果所有列都沒有允許存儲NULL 則NULL值列表不存在。
- 3.將允許存儲NULL的列表采用二進制位表示,0表示該列值不為NULL,1則是NULL。注意是逆序。
- 4.規(guī)定了列表表示的二進制必須是整數(shù)個字節(jié),如果NULL值列不夠8則 高位設置為0補齊。
記錄頭信息
- 1.由固定的5個字節(jié)組成,40個位可以表示40個不同的意思
記錄的真實數(shù)據(jù)
- 1.除了正常的列信息之外,還會包含一些隱藏列
- 2.row_Id(如果不存在主鍵或者其他的唯一索引,則有該列)
- 3.tracsaction_id(事務id)
- 4.roll_pointer,指向了歷史記錄,可以幫助回滾和實現(xiàn)MVCC
CHAR(M)列的存儲格式
- 1.當char列采用的是定長字符集時候,該列占用的字節(jié)數(shù)不會被加到變長字段長度列表,否則會加入
- 2.比如采用了ascii就不需要加入,采用UTF-8就需要加入
- 3.變長字符集的CHAR(M)類型的列要求至少占用M個字節(jié),比如我們采用UTF-8編碼CHAR(10),那么字節(jié)長度可能為10-30,所以我們存入空字符也會占用10個字節(jié)
- 4.上述的好處是如果記錄更新,可以直接利用該存儲空間,無需重新分配。(當然如果如果更新記錄超過10個字節(jié)還是需要重新開辟)
注意了在Compact行格式下,Mysql的列有變長類型,同時對列的編碼字符集也有動態(tài)的,只有是定長類型的列并且也采用定長類型的編碼列,占用分配空間大小才固定。
Redundant行格式
- 1.主要是兩部分:記錄的額外信息和記錄的真實數(shù)據(jù)。
- 2.額外信息:字段長度偏移列表,記錄頭信息
- 3.記錄的真是數(shù)據(jù)。
字段長度偏移列表
- 1.記錄所有字段包含隱藏列的長度信息,逆序。
記錄頭信息
- 1.Redundant行格式多了n_field和1byte_offs_flag這兩個屬性。
- 2.Redundant行格式?jīng)]有record_type這個屬性。
- 3.1byte_offs_flag:標記字段長度偏移列表中每個列對應的偏移量是使用1字節(jié)還是2字節(jié)表示的
- 4.1byte_offs_flag的值是怎么選擇的:根據(jù)該條Redundant行格式記錄的真實數(shù)據(jù)占用的總大小來判斷的:當記錄的真實數(shù)據(jù)占用的字節(jié)數(shù)不大于127(十六進制0x7F,二進制01111111)時,每個列對應的偏移量占用1個字節(jié)
,當記錄的真實數(shù)據(jù)占用的字節(jié)數(shù)大于127,但不大于32767(十六進制0x7FFF,二進制0111111111111111)時,每個列對應的偏移量占用2個字節(jié)。
大于32767的情況存放到了溢出頁中 - 5.在Redundant行格式下 多余字節(jié)存放溢出頁,在本頁只保留前768個字節(jié)和20個字節(jié)的溢出頁面地址,因此只需要2個字節(jié)記錄偏移量
Redundant行格式中NULL值的處理
- 1.因為Redundant行格式并沒有NULL值列表
- 將列對應的偏移量值的第一個比特位作為是否為NULL的依據(jù),該比特位也可以被稱之為NULL比特位
- 3.正是因為首位被做Null比特位了所以才在大于127的時候使用2個字節(jié)來記錄長度
- 4.如果NULL列采用定長字符集來存儲,則采用0x00字節(jié)填充,如果是變長則不占用任何存儲空間
CHAR(M)列的存儲格式
- 1.Compact行格式在CHAR(M)類型的列中存儲數(shù)據(jù)的時候還挺麻煩,分變長字符集和定長字符集的情況
- 2.而在Redundant行格式中十分干脆,不管該列使用的字符集是啥,只要是使用CHAR(M)類型,占用的真實數(shù)據(jù)空間就是該字符集表示一個字符最多需要的字節(jié)數(shù)和M的乘積
- 3.因此不會產(chǎn)生碎片
行溢出數(shù)據(jù)
- 1.除了BLOB或者TEXT類型的列之外,其他所有的列(不包括隱藏列和記錄頭信息)占用的字節(jié)長度加起來不能超過65535個字節(jié)
- 2.這個65535個字節(jié)除了列本身的數(shù)據(jù)之外,還包括一些其他的數(shù)據(jù)(storage overhead),比如說我們?yōu)榱舜鎯σ粋€VARCHAR(M)類型的列,其實需要占用3部分存儲空間:
真實數(shù)據(jù)
真實數(shù)據(jù)占用字節(jié)的長度
NULL值標識,如果該列有NOT NULL屬性則可以沒有這部分存儲空間 - 3.如果該VARCHAR類型的列沒有NOT NULL屬性,那最多只能存儲65532個字節(jié)的數(shù)據(jù)
- 4.Compact和Reduntant行格式中,對于占用存儲空間非常大的列,在記錄的真實數(shù)據(jù)處只會存儲該列的一部分數(shù)據(jù)
- 5.記錄的真實數(shù)據(jù)處用20個字節(jié)存儲指向這些頁的地址,這些也采用鏈表進行連接。
- 6.不只是 VARCHAR(M) 類型的列,其他的 TEXT、BLOB 類型的列在存儲數(shù)據(jù)非常多的時候也會發(fā)生行溢出。
行溢出的臨界點
- 1.MySQL中規(guī)定一個頁中至少存放兩行記錄
Dynamic(5.7默認)和Compressed行格式
- 1.Dynamic和compact很相似,處理行溢出數(shù)據(jù)時有點兒分歧,只記錄地址,不會記錄部分真實數(shù)據(jù)。
- 2.Compressed行格式和Dynamic不同的一點是Compressed行格式會采用壓縮算法對頁面進行壓縮,以節(jié)省空間。