innodb是乙個將表中的資料儲存到磁碟上的儲存引擎,所以即使關機後重啟我們的資料還是存在的。而真正處理資料的過程是發生在記憶體中的,所以需要把磁碟中的資料載入到記憶體中,如果是處理寫入或修改請求的話,還需要把記憶體中的內容重新整理到磁碟上。而我們知道讀寫磁碟的速度非常慢,和記憶體讀寫差了幾個數量級,所以當我們想從表中獲取某些記錄時,innodb儲存引擎需要一條一條的把記錄從磁碟上讀出來麼?不,那樣會慢死,
innodb採取的方式是:將資料劃分為若干個頁,以頁作為磁碟和記憶體之間互動的基本單位,innodb中頁的大小一般為 16 kb。也就是在一般情況下,一次最少從磁碟中讀取16kb的內容到記憶體中,一次最少把記憶體中的16kb內容重新整理到磁碟中。
我們平時是以記錄/行為單位來向表中插入資料的,這些記錄在磁碟上的存放方式也被稱為行格式或者記錄格式。設計innodb儲存引擎的大叔們到現在為止設計了4種不同型別的行格式,分別是compact、redundant、dynamic和compressed行格式,隨著時間的推移,他們可能會設計出更多的行格式,但是不管怎麼變,在原理上大體都是相同的。
指定行格式的語法
我們可以在建立或修改表的語句中指定行格式:
create table 表名 (列的資訊) row_format=行格式名稱
alter table 表名 row_format=行格式名稱
演示資料表如下:
大家從圖中可以看出來,一條完整的記錄其實可以被分為記錄的額外資訊和記錄的真實資料兩大部分,下邊我們詳細看一下這兩部分的組成。
這部分資訊是伺服器為了描述這條記錄而不得不額外新增的一些資訊,這些額外資訊分為3類,分別是變長字段長度列表、null值列表和記錄頭資訊,我們分別看一下。
我們知道mysql支援一些變長的資料型別,比如varchar(m)、varbinary(m)、各種text型別,各種blob型別,我們也可以把擁有這些資料型別的列稱為變長字段,變長字段中儲存多少位元組的資料是不固定的,所以我們在儲存真實資料的時候需要順便把這些資料占用的位元組數也存起來,這樣才不至於把mysql伺服器搞懵,
所以這些變長字段占用的儲存空間分為兩部分:
真正的資料內容
占用的位元組數
在compact行格式中,把所有 變長字段的真實資料占用的位元組長度都存放在記錄的開頭部位,從而形成乙個變長字段長度列表,各變長欄位資料占用的位元組數按照列的順序逆序存放,我們再次強調一遍,是逆序存放!
我們拿record_format_demo表中的第一條記錄來舉個例子。因為record_format_demo表的c1、c2、c4列都是varchar(10)型別的,也就是變長的資料型別,所以這三個列的值的長度都需要儲存在記錄開頭處,因為record_format_demo表中的各個列都使用的是ascii字符集,所以每個字元只需要1個位元組來進行編碼,來看一下第一條記錄各變長欄位內容的長度:
又因為這些長度值需要按照列的逆序存放,所以最後變長字段長度列表的位元組串用十六進製制表示的效果就是(各個位元組之間實際上沒有空格,用空格隔開只是方便理解):
01
0304
把這個位元組串組成的變長字段長度列表填入上邊的示意圖中的效果就是:
由於第一行記錄中c1、c2、c4列中的字串都比較短,也就是說內容占用的位元組數比較小,用1個位元組就可以表示,但是如果變長列的內容占用的位元組數比較多,可能就需要用2個位元組來表示。具體用1個還是2個位元組來表示真實資料占用的位元組數,innodb有它的一套規則,我們首先宣告一下w、m和l的意思:
1、假設某個字符集中表示乙個字元最多需要使用的位元組數為w,也就是使用show charset語句的結果中的maxlen列,比方說utf8字符集中的w就是3,gbk字符集中的w就是2,ascii字符集中的w就是1。
2、對於變長型別varchar(m)來說,這種型別表示能儲存最多m個字元(注意是字元不是位元組),所以這個型別能表示的字串最多占用的位元組數就是m×w。
3、假設它實際儲存的字串占用的位元組數是l。
所以確定使用1個位元組還是2個位元組表示真正字串占用的位元組數的規則就是這樣:
3.1、如果m×w <= 255,那麼使用1個位元組來表示真正字串占用的位元組數。也就是說innodb在讀記錄的變長字段長度列表時先檢視表結構,如果某個變長字段允許儲存的最大位元組數不大於255時,可以認為只使用1個位元組來表示真正字串占用的位元組數。
3.2、如果m×w > 255,則分為兩種情況:
3.2.1、如果l <= 127,則用1個位元組來表示真正字串占用的位元組數。
3.2.2、如果l > 127,則用2個位元組來表示真正字串占用的位元組數。
innodb在讀記錄的變長字段長度列表時先檢視表結構,如果某個變長字段允許儲存的最大位元組數大於255時,該怎麼區分它正在讀的某個位元組是乙個單獨的字段長度還是半個字段長度呢?設計innodb的大叔使用該位元組的第乙個二進位制位作為標誌位:如果該位元組的第乙個位為0,那該位元組就是乙個單獨的字段長度(使用乙個位元組表示不大於127的二進位制的第乙個位都為0),如果該位元組的第乙個位為1,那該位元組就是半個字段長度。 對於一些占用位元組數非常多的字段,比方說某個字段長度大於了16kb,那麼如果該記錄在單個頁面中無法儲存時,innodb會把一部分資料存放到所謂的溢位頁中(我們後邊會嘮叨),在變長字段長度列表處只儲存留在本頁面中的長度,所以使用兩個位元組也可以存放下來。總結一下就是說:如果該可變字段允許儲存的最大位元組數(m×w)超過255位元組並且真實儲存的位元組數(l)超過127位元組,則使用2個位元組,否則使用1個位元組。
另外需要注意的一點是,變長字段長度列表中只儲存值為 非null 的列內容占用的長度,值為 null 的列的長度是不儲存的 。也就是說對於第二條記錄來說,因為c4列的值為null,所以第二條記錄的變長字段長度列表只需要儲存c1和c2列的長度即可。其中c1列儲存的值為』eeee』,占用的位元組數為4,c2列儲存的值為』fff』,占用的位元組數為3。數字4可以用1個位元組表示,3也可以用1個位元組表示,所以整個變長字段長度列表共需2個位元組。填充完變長字段長度列表的兩條記錄的對比圖如下:
小貼士: 並不是所有記錄都有這個 變長字段長度列表 部分,比方說表中所有的列都不是變長的資料型別的話,這一部分就不需要有。
MySQL InnoDB 儲存結構
mysql innodb 儲存結構 innodb儲存引擎的關鍵特性包括 插入緩衝 insert buffer 兩次寫 double write 自適應雜湊索引 adaptive hash index 非同步io async io 重新整理鄰接頁 從邏輯上講 所有的資料都被邏輯的放在乙個空間中,稱為t...
MySQL Innodb儲存結構
上圖擷取網際網路 innodb儲存引擎中的資料按照表空間進行管理。在初始化時建立乙個共享表空間 ibdata1 他存放innodb的所有資料 系統表,回滾 undo 資訊 5.7可以單獨配置undo表空間 插入緩衝索引頁 系統事務資訊,二次寫緩衝 若開啟了innodb file per table引...
MySQL InnoDB邏輯儲存結構
innodb儲存引擎中的表非常像oracle中的索引組織表,每張表必須得有主鍵,如果表在建立時沒有顯示定義主鍵,則根據以下原則自動建立主鍵 1 如果有非空的唯一索引,則該索引所在的列為主鍵 2 如果不符合上述條件,自動建立乙個6個位元組的指標為主鍵。innodb儲存引擎的邏輯儲存結構和oracle幾...