(本片博文是《MySQL技術(shù)內(nèi)幕 InnoDB存儲引擎第二版》的讀書筆記)
索引組織表
InnoDB存儲引擎中驼鹅,表都是按照主鍵順序組織存放的塌计,這種存儲方式的表稱為索引組織表(index organized table)屯耸。InnoDB存儲引擎表中,每張表都有個主鍵槽袄,若在創(chuàng)建表時沒有顯式定義主鍵則InnoDB存儲引擎會按如下方式選擇或者創(chuàng)建主鍵:
- 首先判斷表中是否有非空的唯一索引(Unique NOT NULL)凡人,如果有,則該列即為主鍵坪哄。(當(dāng)表中有多個非空唯一索引時质蕉,InnoDB存儲引擎將選擇建表時第一個定義的非空唯一索引為主鍵,注意是根據(jù)定義索引的順序而不是建表時列的順序)翩肌。
- 如果不符上述條件模暗,則會自動創(chuàng)建一個6字節(jié)大小的指針。
InnoDB邏輯存儲結(jié)構(gòu)
這是《MySQL技術(shù)內(nèi)幕 InnoDB存儲引擎》一書中對InnoDB邏輯存儲結(jié)構(gòu)的描述念祭。
表空間
表空間是Innodb存儲引擎邏輯的最高層兑宇,所有的數(shù)據(jù)都存放在表空間中,默認(rèn)情況下粱坤,Innodb存儲引擎有一個共享表空間ibdata1,即所有數(shù)據(jù)都存放在這個表空間中內(nèi)隶糕。如果啟用了innodb_file_per_table參數(shù),則每張表內(nèi)的數(shù)據(jù)可以單獨放到一個表空間內(nèi)站玄,但請注意枚驻,只有數(shù)據(jù)、索引株旷、和插入緩沖Bitmap放入單獨表內(nèi)再登,其他數(shù)據(jù),比如回滾(undo)信息灾常、插入緩沖檢索頁霎冯、系統(tǒng)事物信息铃拇,二次寫緩沖等還是放在原來的共享表內(nèi)的钞瀑。
段
從上圖中可以看出表空間由段組成,常見的段有數(shù)據(jù)段慷荔、索引段雕什、回滾段等。因為InnoDB存儲引擎表是索引組織的,因此數(shù)據(jù)即索引贷岸,索引即數(shù)據(jù)壹士。數(shù)據(jù)段即為B+樹的葉子結(jié)點,索引段即為B+樹的非索引結(jié)點偿警。在InnoDB存儲引擎中對段的管理都是由引擎自身所完成躏救,DBA不能也沒必要對其進行控制。
區(qū)
區(qū)是由連續(xù)頁組成的空間螟蒸,在任何情況下每個區(qū)的大小都為1MB盒使。為了保證區(qū)中頁的連續(xù)性,InnoDB存儲引擎一次從磁盤申請4~5個區(qū)七嫌。默認(rèn)情況下少办,InnoDB存儲引擎頁的大小為16KB,一個區(qū)中一共64個連續(xù)的區(qū)诵原。
頁(塊)
頁是InnoDB磁盤管理的最小單位英妓。在InnoDB存儲引擎中,默認(rèn)每個頁的大小為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)如下圖:
其中File Header墨状、Page Header、File Trailer的大小是固定的菲饼,分別為38肾砂,56,8字節(jié)宏悦,這些空間用來標(biāo)記該頁的一些信息镐确,如Checksum包吝,數(shù)據(jù)頁所在B+樹索引的層數(shù)等。User Records源葫、Free Space诗越、Page Directory這些部分為實際的行記錄存儲空間,因此大小是動態(tài)的息堂。
1.File Header用來記錄頁的一些頭信息嚷狞,由表中8個部分組成,共占38字節(jié)荣堰。各部分代表信息如下表所示:
2.Page Header用來記錄數(shù)據(jù)頁的狀態(tài)信息感耙,14個部分組成,共占56字節(jié)持隧。各部分代表信息如下表所示:
3.Infimum和Supermum Record是InnoDB中兩個虛擬的行記錄即硼,用來限定記錄的邊界。Infimum記錄是比該頁中任何主鍵值都要小的值屡拨,Supermum是比任何可能大的值還要大的值只酥。這兩個記錄在頁創(chuàng)建時被建立,并且在任何情況下都不會被刪除呀狼。下圖顯示了Infimum記錄和Supermum記錄:
4.User Record和Free Space
User Record就是實際存儲行記錄的內(nèi)容裂允。InnoDB存儲引擎表總是B+樹索引組織的。Free Space指空閑空間哥艇,是鏈表數(shù)據(jù)結(jié)構(gòu)绝编,在一條記錄被刪除后,該空間會被加入到空閑鏈表中貌踏。
5.Page Directory
Page Directory中存放了記錄的相對位置(是頁相對位置而不是偏移量)十饥,有時這些記錄指針稱為Slots(槽)或者目錄槽(Directory Slots)。與其他數(shù)據(jù)庫系統(tǒng)不同的是祖乳,在InnoDB中并不是每個記錄擁有一個槽逗堵,InnoDB存儲引擎的槽是一個稀疏目錄(sparse directory),即一個槽中可能包含多個紀(jì)錄眷昆。偽記錄的Infimum的n_owned值總是為1蜒秤,記錄Supermum的n_owned的取值范圍為[1,8]亚斋,其他用戶記錄n_owned的取值范圍為[4作媚,8]。當(dāng)記錄被插入或刪除時需要對槽進行分裂或平衡的維護操作帅刊。
在Slots中記錄按照索引鍵值順序存放纸泡,這樣可以利用二叉查找迅速找到記錄的指針。假設(shè)有('i','d','c','b','e','g','l','h','f','j','k','a')厚掷,同時假設(shè)一個槽中包含4條記錄弟灼,則Slots中的記錄可能是('a','e','i')。
由于在InnoDB存儲引擎中Page Directory是稀疏目錄冒黑,二叉查找的結(jié)果只是一個粗略結(jié)果田绑,因此InnoDB存儲引擎必須通過recorder header中的next_record來繼續(xù)查找相關(guān)記錄。同時抡爹,Page Directory很好地解釋了recorder header中的n_owned值的含義掩驱,因為這些記錄并不包括在Page Directory中。
B+樹索引本身并不能找到具體的一條記錄冬竟,能找到只是改記錄所在的頁欧穴。數(shù)據(jù)庫把頁載入到內(nèi)存,然后通過Page Directory再進行二叉查找泵殴。只不過二叉查找的時間復(fù)雜度很低涮帘,同時在內(nèi)存中的查找很快,因此通常忽略這部分查找所用的時間笑诅。
6.File Trailer
該部分是為了檢測頁是否已經(jīng)完整地寫入磁盤的(可能在寫入過程中磁盤損壞调缨、機器關(guān)機等)。該部分占用8字節(jié)吆你,前4字節(jié)代表該頁的checksum值弦叶,后4字節(jié)和File Header中的FIL_PAGE_LSN相同。將這兩個值與File Header中的FIL_PAGE_SPACE_OR_CHKSUM和FIL_PAGE_LSN值進行比較妇多,看是否一致(checksum的比較需要通過InnoDB的checksum函數(shù)來比較伤哺,不是簡單的等值比較),以此來保證頁的完整新者祖。
行
InnoDB存儲引擎是面向列的立莉,也就是說數(shù)據(jù)按行存放。每個頁存放的行記錄也是有硬性定義的七问,最多允許存放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行記錄格式
上圖就是Compact行記錄的存儲方式葛账。由圖可知柠衅,首部是一個非NULL變長字段長度列表,并且是按照列的順序逆序放置的籍琳,長度為:
- 若列的長度小于255字節(jié)菲宴,用1字節(jié)表示贷祈;
- 若大于255字節(jié),用2字節(jié)表示喝峦。
變長字段的長度最大不可以超過2字節(jié)势誊,因為在MySQL中VARCHAR類型的最大長度限制為65535。變長字段之后的第二個部分是NULL標(biāo)志位谣蠢,指示了該行數(shù)據(jù)中是否有NULL值粟耻,有則用1表示,占1字節(jié)眉踱。接下來是記錄頭信息挤忙,固定占用5字節(jié)。每位的含義見下表
最后的部分就是實際存儲每個列的數(shù)據(jù)谈喳。NULL不占該部分任何空間册烈,即NULL除了占有NULL標(biāo)志位,實際存儲不占有任何空間婿禽。另外有一點需要注意的是茄厘,每行數(shù)據(jù)除了用戶定義的列外,還有兩個隱藏列谈宛,事務(wù)ID和回滾指針列次哈,分別為6字節(jié)和7字節(jié)的大小。若InnoDB表沒有定義主鍵吆录,每行還會增加一個6字節(jié)的rowid列窑滞。
Redundant行記錄格式
Redundant是MySQL5.0版本之前InnoDB的行記錄存儲方式,MySQL5.0支持Redundant是為了兼容之前版本的頁格式恢筝。Redundant行記錄采用如圖所示的方式存儲哀卫。
首部是字段長度偏移列表,按照列的順序逆序放置撬槽。若列的長度小于255字節(jié)此改,用1字節(jié)表示;若大于255字節(jié)侄柔,用2字節(jié)表示共啃。第二部分為記錄頭信息,占用6字節(jié)暂题,每位含義如下表
從表中可以看出移剪,n_fields值代表一行中列的數(shù)量,占用10位薪者,很好的解釋了為什么MySQL數(shù)據(jù)庫一行支持最多的列為1023纵苛。另一個需要知道的是1byte_offs_flags,該值定義了偏移列表占用1字節(jié)還是2字節(jié)。最后的部分就是實際存儲的每個列的數(shù)據(jù)了攻人。