1. InnoDB、行格式
InnoDB是Mysql默認的存儲引擎,由于磁盤本身的讀寫速度很慢,InnoDB將數(shù)據(jù)劃分為若干個頁,以頁作為磁盤和內(nèi)存之間交互的基本單位,InnoDB中頁的大小一般為 16 KB。也就是說,通常情況下,InnoDB一次最少從磁盤中讀取16KB數(shù)據(jù)到內(nèi)存中,一次最少從內(nèi)存中寫16KB數(shù)據(jù)到磁盤上。
在InnoDB中,數(shù)據(jù)存儲在硬盤上的格式被稱為“行格式”或“記錄格式”,InnoDB擁有四種不同的“行格式”。
- COMPACT
直接看圖:

什么是變長字段長度列表?
一些數(shù)據(jù)類型可以用來指定變長字符(即表示一個字符需要的字節(jié)數(shù)不確定,比如在utf8字符集中),比如varchar、text等,我們需要將該數(shù)據(jù)真實占用的字節(jié)長度記錄下來(注意,變長字段長度列表存儲的是字節(jié)長度,而不是字節(jié)本身)并將各變長字段長度以逆序排列。
什么是NULL值列表?
將值為NULL的列統(tǒng)一管理起來,存儲到NULL值列表中。如何記錄呢?將每個可以為NULL值的列對應(yīng)一個二進制位,若該位置為1,則代表相應(yīng)的列值為NULL。
NULL值列表必須為整數(shù)個字節(jié),若不足一個字節(jié),則在高位補0。比如一張表只有三個字段,那么NULL值列表也必須要有一個字節(jié)(8個二進制位),其中前5位為0,后3位記錄字段的值是否為NULL。
什么是記錄頭信息?
記錄頭信息包含了一些當(dāng)前記錄的信息,占用5個字節(jié)。具體有哪些信息?如下:
delete_mask:標(biāo)記是否已經(jīng)被刪除。是的,即使一條數(shù)據(jù)被刪除,這里很可能只是邏輯刪除,它在物理上也可以依然可以留存在頁中。這些被刪除的記錄之所以不立即從磁盤上移除,是因為移除它們之后把其他的記錄在磁盤上重新排列需要性能消耗,所以只是打一個刪除標(biāo)記而已,所有被刪除掉的記錄都會組成一個所謂的垃圾鏈表,在這個鏈表中的記錄占用的空間稱之為所謂的可重用空間,之后如果有新記錄插入到表中的話,可能把這些被刪除的記錄占用的存儲空間覆蓋掉。
min_rec_mask:B+樹的每層非葉子節(jié)點中的最小記錄都會添加該標(biāo)記。
n_owned:
heap_no:表示當(dāng)前記錄在頁中的位置。
record_type:表示當(dāng)前記錄的類型。0表示普通記錄,1表示B+樹非葉節(jié)點記錄,2表示最小記錄,3表示最大記錄。
next_record:表示從當(dāng)前記錄的真實數(shù)據(jù)到下一條記錄的真實數(shù)據(jù)的地址偏移量。(注意這里指向的是真實數(shù)據(jù)的開頭,不是完整數(shù)據(jù),完整數(shù)據(jù)包含真實數(shù)據(jù)和額外信息)
隱藏列?
除了我們存儲的數(shù)據(jù)外,Mysql還會默認添加一些列,被稱為隱藏列,隱藏列包含:行ID、事務(wù)ID、回滾指針。
有同學(xué)可能有疑問?每行數(shù)據(jù)不是有自定義的主鍵作為行ID嗎?為什么隱藏列還要生成?InnoDB的主鍵策略是:優(yōu)先使用自定義主鍵,若未自定義,則選用一個Unique鍵作為主鍵,若Unique鍵也未設(shè)置,這才使用隱藏列中的行ID。
其余三種行格式有:Redundant、Dynamic、Compressed。不再一一細看了,需要用到時可以再針對性查資料。
2. 頁
上面提到了“頁”這個概念,它是管理內(nèi)存和磁盤之間數(shù)據(jù)的基本單位。它的組成部分如圖所示:

每插入一條記錄,InnoDB將會從Free Space申請一塊空間交付給User Records使用,存儲我們的數(shù)據(jù)。如果Free Space用完,那么將要申請一塊新的頁。
3. 頁目錄
每條記錄行在User Records中是以鏈表形式,以主鍵為順序排列的,那如果我要查找其中主鍵為n的一條記錄呢?
最笨的辦法就是遍歷,但這種方法一旦面對大量的數(shù)據(jù)無疑效率低下。為了解決這個問題,InnoDB設(shè)計了頁目錄。具體是如何設(shè)計的呢?如下:
首先,將所有的記錄分成若干個組;每個組的最后一條記錄的頭信息中的n_owned屬性表示該組擁有多少條記錄;將每個組最后一條記錄的地址偏移量(主要目的是為了計算個數(shù))提取出來,這些偏移量被稱為“槽”(slot),存儲在頁的尾部,這個地方就是頁目錄(Page Diretory)。
從邏輯上簡單來說,就是將所有記錄分組,然后將每組的偏移量與個數(shù)存入頁目錄,每組的記錄個數(shù)一般在1-8之間。
所以在插入數(shù)據(jù)時,首先通過主鍵找到合適的槽,該槽對應(yīng)的n_owned值加一,一旦大于8,則分裂為兩個槽。
如果要查找主鍵為n的記錄,先通過二分法找到相應(yīng)的槽,然后再遍歷這個槽。