一文讲清,MySQL数据库一行数据在磁盘上是怎么存储的?
數據庫給使用者最直觀的感覺,就是庫、表、行、字段,這些概念都是邏輯上的。前面我們深入講解了Buffer Pool的內部原理,它的基本存儲單位是默認大小為16K的頁。每頁都保存了一行一行的數據。我們按照數據頁為單位把磁盤上的數據加載到內存的緩存頁里來,也是按照頁為單位,把緩存頁的數據刷入磁盤的數據頁中。
而我們常常聽到數據頁、數據區、表空間這些名詞,其實這些名詞是物理層面上的概念。我們不經要問,庫、表、行、字段,這些邏輯上的概念是如何對應到物理層的概念上的呢?我們查詢一行數據,是如何找到條數據所在的數據頁的呢?
接下來,筆者用幾篇文章,講解下MySQL的表空間、數據區、數據頁這些概念,當大家明白搞明白這些東西后,就自然理解上面的問題了。
行格式
行格式即行記錄的物理存儲格式,決定了這張表數據的物理存儲方式,會影響crud性能。
指定行格式
CREATE TABLE 表名 (列的信息) ROW_FORMAT=行格式名稱; ALTER?TABLE?表名?ROW_FORMAT=行格式名稱;InnoDB 包含以下四種行格式
Compact
Redundant
Dynamic
Compressed
mysql5.7之前的版本使用的是compact行格式,5.7及以后的版本使用的是dynamic行格式。Compact和Dynamic應用較廣泛,compact是目前使用最多的一種,而dynamic是新版本默認的行記錄格式。
初識MySQL行數據存儲格式
我們這里就以Compact存儲格式來講解。
Compact行格式下,每行數據的存儲格式,大概是這樣的:
變長字段(記錄的長度)列表,null值列表,數據頭,col1的值,col2的值,col3的值......
Compact 中一條完整的記錄可以被分成'記錄的額外信息'和'記錄的真實數據' 兩部分,其他三種存儲存儲格式基本大同小異。
變長字段是如何存儲的?
MySQL中變長字段長度是不固定的,?比如VARCHAR(50),實際存儲的內容可能是"hello",也可能是“hello world"。
所以,我們要讀取這類數據字段,必須要知道它的長度。
比如一行數據,它的幾個字段為VARCHAR(10), VARCHAR(5)
,VARCHAR(20),CHAR(1),CHAR(1),插入一行數據:hello, ni, hao, a, b。
此時磁盤中存儲的行開頭的變長字段長度列表,必須存儲幾個變長字段的長度,需要注意的是,這里是逆序存儲的。
行數據存儲格式是這樣的:
0x03 0x02 0x05 null值列表 頭字段?hello, ni, hao,?a, b
NULL值列表
null值列表,說的就是一行數據里可能有的字段是選填的,值可以是null。比如name字段,如果允許為null,那么實際存儲的時候,要標記出來。
為了節省存儲空間,MySQL設計的時候,使用二進制的bit位來存儲null值列表。
假如創建了一張表:
CREATE TABLE user ( name VARCHAR(10)NOT NULL,address VARCHAR(20),gender CHAR(1),job VARCHAR(30),school VARCHAR(50) )ROW_FORMAT = COMPACT;user表5個字段,4個變長的,只有name是非NULL的,其他的4個字段都是可以為NULL的。
假如現在插入一行數據:zhangsan NULL M NULL Tsinghua,address和job是NULL,那么它在磁盤上是怎么存儲的?
NULL值列表,是這樣存儲的,你所有允許值為NULL的字段,都會有一個二進制的bit值,bit值為1說明是NULL,如果bit值為0說明不是NULL。
上面插入的那行數據,address、gender、job、school4個字段都允許為NULL,每個字段都會有一個bit位,其中address與job是NULL,所有4個bit位應該是:1010。
而且NULL值列表也是逆序存儲的,所以NULL值列表里是0101。
一般NULL值列表是8個bit位的倍數,如果不足8個bit位,則高位補0。
所以現在看來,行數據存儲格式是這樣的:
0x08 0x08?00000101 頭信息?col1的值,col2的值,col3的值......
數據頭以及真實數據
行數據的頭長度是固定的40bit,第一個bit和第二個bit,都是預留的,暫時沒有用到。
記錄頭信息詳細如下表所示,有的暫時沒有用到,可以暫時不用深究它。
現在再加上數據頭部分,上面的行存儲格式就變成這樣了,
0x08 0x08?00000101 0000000000000000000010000000000000011001 zhangsan M Tsinghua
剛開始先是變長字段的長度,用16進制表示,然后是NULL值列表,標識哪些值是NULL,接著是40bit的數據頭,最后是真實數據。
在讀取數據的時候,也會根據變長字段的長度,先讀取出長度為8的zhangsan。
然后發現第二個字段是NULL,就不用再讀了。
第三個字段是定長的1,就直接讀取出gender為M。
第四個字段是NULL,就不用再讀了。
第五個字段變長,長度是8,再讀取長度為8的Tsinghua。
然而,真正磁盤上存儲的時候,那些字符串就是直接存儲在磁盤上的嗎?
實際上,字符串都是根據數據庫指定的字符集編碼,進行編碼之后再存儲的,編碼后大概是這樣的:
0x08 0x08?00000101?0000000000000000000010000000000000011001?341324?134546 9342345
大家會看到上面,字符串和其他類型的數值最終都會根據數據庫字符集編碼,變成一些數字和符號存儲在磁盤上。
最后,在實際存儲一行數據的時候,MySQL還會給每條記錄,加入一些隱藏字段。如下表:
如果用戶沒有指定主鍵,且表中沒有Unique鍵時才會使用DB_ROW_ID作為主鍵。
最終,上面那條數據的存儲格式,變成這樣了:
0x08 0x08?00000101?0000000000000000000010000000000000011001?00000000034C(DB_ROW_ID)00000000036D(DB_TRX_ID) EA000010022B(DB_ROL_PTR) 341324?134546 9342345
到這里為止,我們基本把一行數據在磁盤上是如何存儲的講清楚了。
有道無術,術可成;有術無道,止于術
歡迎大家關注Java之道公眾號
好文章,我在看??
總結
以上是生活随笔為你收集整理的一文讲清,MySQL数据库一行数据在磁盘上是怎么存储的?的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 阿里云的这个智能编码插件真心好用!Jav
 - 下一篇: 3. 机器学习中为什么需要梯度下降_机器