MySQL InnoDB行記錄存儲結構分析
Mysql的存儲行為是由Innodb存儲引擎去具體實現的,在windows下安裝Mysql后有data(數據庫存放的地方)的文件夾,linux一般在/var/lib/mysql文件件。
創建數據庫和表后我們可以在data目錄先看到數據庫對應名稱文件夾,文件夾有opt、frm、ibd三種文件:
db.opt,用來存儲當前數據庫的默認字符集和字符校驗規則。demo1.frm ,t_order 的表結構會保存在這個文件demo1.ibd,t_order 的表數據會保存在這個文件。表數據既可以存在共享表空間文件(文件名:ibdata1,在data目錄下)里,也可以存放在獨占表空間文件(文件名:表名字.ibd)先看圖,先對表空間結構做個大概了解,形成一個概念
InnoDB存儲引擎中,對段的管理都是由引擎自身所完成,我們已看到段有幾種類型,它是不同類型的區組成的集合,一般分為索引段(B+樹非葉子節點區)、數據段(B+樹非葉子節點區)、回滾段(回滾數據區)。
也就是說InnoDB 對 B+ 樹的葉節點和葉子節點進行了區別對待,也就是說葉子節點有自己獨有的區,非葉子節點也有自己獨有的區,如果不區分葉子節點和非葉子節點,統統把節點代表的頁面放到申請到的區中的話,進行范圍掃描的效率就大幅降低,而不同的區的集合就組成了不同的段。
區我們知道B+樹的每一層中的頁都會形成一個雙向鏈表,如果是以頁為單位來分配存儲空間的話,雙向鏈表相鄰的兩個頁之間的物理位置可能不是連續的,也許離得非常遠,這種情況下進行 隨機I/O 是會很慢的。
因此,應該盡量讓鏈表中相鄰的頁的物理位置也相鄰,這樣進行范圍查詢的時候才可以使用所謂的 順序I/O。
區在物理位置上由連續的64個頁組成,InnoDB 中的頁大小默認是 16KB,所以一個區的大小是 64*16KB= 1MB,這樣使得頁的雙向鏈表在物理位置也是相鄰的,從而進行順序I/O,加快了查詢效率!
在表數據量大的時候,為某個索引分配空間的時候就不再按照頁為單位分配了,而是按區為單位分配,甚至在表中的數據特別多的時候,可以一次性分配多個連續的區。
頁Innodb讀取數據的時候,并不是按照行來讀取數據的,InnoDB 的數據是按【頁】為單位來讀寫的,當需要讀一條記錄的時候,并不是將這個行記錄從磁盤讀出來,而是以頁為單位,將其整體讀入內存。
InnoDB 的數據是按【頁】為單位來讀寫的,也就是說,當需要讀一條記錄的時候,并不是將這個行記錄從磁盤讀出來,而是以頁為單位,將其整體讀入內存。默認每個頁的大小為 16KB,也就是最多能保證 16KB 的連續存儲空間。頁是 InnoDB 存儲引擎磁盤管理的最小單元,數據庫每次讀寫都是以【頁】為單位的,一次最少從磁盤中讀取 16K 的內容到內存中。行MySQL也是以【行 row】進行存儲的,圖中對于行的描畫圖是 COMPACT格式,這也是重點需要了解的格式,而不同的行格式,存儲的結構也不同。
InnoDB 行格式類型行格式:就是記錄在磁盤上的存放形式或者說存儲結構
InnoDB 存儲引擎設計了 4 種行格式,分別是 Redundant、Compact、Dynamic和 Compressed ,后三個都是緊湊型行格式,為的是存放更多的行記錄。
Redundant 行格式比較古老了, MySQL 5.0 版本之前用的行格式,現在基本不用了,我們知道有這個格式就行了
Compact 行格式在MySQL 5.0 之后引入,在MySQL5.1版本中,默認設置為Compact行格式,一條完整的記錄其實可以被分為記錄的額外信息和記錄的真實數據兩大部分。
Dynamic 和 Compressed 它們的行格式都和 Compact 挺像,只是在 處理溢出列數據和Compact不同 ,MySQL5.7 版本之后,默認使用 Dynamic 行格式。
Compact 行格式圖解從上面我們知道Compact和Dynamic 和 Compressed很像,那么我們就Compact行格式展開進一步了解,了解了Compact就等同于對其他也做了了解。
從圖中我們可以看到Compact行格式下,一條記錄分為 【記錄的額外信息】和【記錄的真實數據】兩部分,我們的列數據是在真實數據部分,我們再分別對這些內容進行更具體的描述。
記錄的額外信息額外信息為的是更好的管理記錄,分為變長字段長度列表、NULL值列表、記錄頭信息
我們來創建一個表來看看變長字段具體是存的,表結構如下,行格式 Compact,本文對于行記錄的實際存儲案例基于這張表:
CREATE TABLE `demo1` ( `id` int(11) NOT NULL AUTO_INCREMENT, `col1` varchar(45) COLLATE utf8_bin DEFAULT NULL, `col2` varchar(45) COLLATE utf8_bin DEFAULT NULL, `col3` int(11) DEFAULT NULL, `col4` char(5) COLLATE utf8_bin DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=ascii ROW_FORMAT=COMPACT;并插入三條數據,demo1表中的各個列都使用的是ascii字符集(每個字符只需要1個字節來進行編碼)
1:變長字段列信息
針對VARCHAR、TEXT、BLOB這類變長字段,列中實際存儲了多少數據是不固定的,因此除了要把數據本身存下來,還需要記下它的長度,COMPACT將變長列的實際長度按照字段的順序,逆序存儲在變長字段長度列表里。
變長字段存儲空間分為兩部分:真正的數據部分、該數據占用的字節數
從demo1表的第一條記錄來看各個字段占用的字節數,因為是變長字段, id、col3(int)、col(char)這三個字段可以不用管
clo1字段是varchar ,值是zs,占用兩個字節的空間,十六進制 0x02;clo2字段是varchar ,值是lsa,占用三個字節的空間,十六進制 0x03;第一行行記錄填入變長字段長度列表后的示意圖如下:
逆序排列的目的是為了讓位置靠前的記錄的真實數據和數據對應的字段長度信息可以同時在一個 CPU Cache Line 中,這樣就可以提高 CPU Cache 的命中率
2:NULL值列表
當某些字段是null值時,才顯示在null值列表null值列表是通過bit位來進行標識的,一個字段占一個比特位,bit位按字段逆序排列字段值為null的bit位為1,否則為0null 值列表必須用整數個字節的位表示(1字節8位),如果使用的二進制位個數不足整數個字節,則在字節的高位補 0要注意的是null值列表并不是固定的1個字節,如果一條記錄中有9個字段的值都是null,那么null值列表大小將是兩個字節大小,依次類推。
結合這些特性,我們來看看一條記錄中存在null值和不存在null值在null值列表中的樣子,我們記錄使用上面表demo1的結構和數據,其中id是主鍵不能為null,不在討論范圍內,表中null字段不超過8個,這三條記錄對應的null值列表如下:
第一條記錄:
第二條記錄:
第三條記錄:
3:記錄頭信息
記錄頭其實包含了很多信息,如圖,我們著重了解紅色部分幾個比較重要的。
我們看隱藏字段 row_id、trx_id、roll_ptr 感覺是不是在哪里遇到過,只要你了解過Mysql的MVCC機制就很熟悉這幾個字段
其他字段就是我們創建表的時候定義的各個列字段了。
總結通篇下來,感覺對InnoDB實際的存儲結構有了更深的認識,當然也會產生不少問題,比如:
1:一行記錄除了 TEXT、BLOBs 類型的列,限制最大為 65535 字節,那么能具體分析分析嗎?
2:行溢出了會怎么樣,因為一頁就16kb,16384字節,是小于65535 字節的
3:為什么設計表的時候字段會選擇not null?
等等,這些問題將會在下次進行總結,就不在這里用大篇幅展開了。
到此這篇關于MySQL InnoDB行記錄存儲結構分析的文章就介紹到這了,更多相關MySQL InnoDB存儲結構內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!
