在開文我先說明一下,接下來的數據庫知識文章都是在微信公眾號“我們都是小青蛙”學習然后在通過自己的理解進行書寫的。有興趣的朋友可以去關注這個微信公眾號。話不多說,我們在日常使用數據庫進行數據持 久化的時候有沒有想過我們的數據在數據庫中是什么樣的儲存結構,我們可能想的最多的是怎樣進行SQL的調優,但是對於數據庫都不熟悉能做到調優設計么?答案顯然是不能!!所以我們在這里開始數據庫的第一 篇文章。數據庫的記錄儲存結構。
我們可能有很多熟悉的數據庫儲存引擎,比如說Inoodb,MyISAM,Memory。每一種儲存引擎對於數據的持久化可能是不同的,比如說我們的Memory儲存引擎的數據都是不會寫進磁盤的,所有的數據是保存在內存 中的,也就意味着如果我們的服務器進行重啟以后,數據是不會被進行保存的。當然,因為MySQL數據庫默認的儲存引擎是使用的Inoodb,所以我們在這里是需要重點介紹這個儲存引擎的。
簡介:
Inoodb儲存引擎是把數據儲存在磁盤里面的儲存引擎,它在內存和磁盤的交互中使用的是頁這個數據單位。我們都知道一個事情就是我們在對磁盤進行訪問的時候速度是非常慢的,所以我們肯定是不能接受一 條數據一條數據的進行取用。所有數據划分為若干頁,一個數據頁是可以保存16kb的數據,也就是說我們每次在進行數據訪問的時候是一次性的16kb數據。
行格式:
知道了數據庫中我們數據的大概儲存方式,那么接下來我們需要做的就是學習一條數據在我們的數據庫中是什么樣的一個結構存在。我們把數據庫儲存數據的格式稱之為行格式或者是記錄格式,我們現在所使用 的行格式有Compact,Redundant,Dynamic,Compressed。當然隨着時間的遷移我們是會有更多的行格式出現。
Compact:
創建語法:我們會在bysj數據庫中創建一個表,test,方便我們接下來對於儲存結構的演示。如下所示,設置行格式我們直接使用ROW_FORMAT=行格式名稱 。我們同時也對編碼格式進行了設置為ASCII,這個 編碼格式只能是儲存空格,字母,標點符號,不可見字符,數字。所以漢字是不可以儲存進來的!
接下來我們插入兩條數據:
結構示意圖:如下圖所示我們可以看到的是分為兩個大部分,第一部分就是記錄額外信息,第二部分就是記錄真實數據,接下來我們對這兩個部分進行詳細的描述:
變長字段長度列表:
這個部分記錄的是可變長字段的信息,比如說VARCHAR,VARBINARY,各種TEXT,BLOB數據類型。我們都知道這些可變長的數據類型是分為兩部分的,第一種就是它們可以儲存的數據最大值,第二種 就是儲存數據的真實大小。MySQL數據庫也不知我們到底儲存了多少內容,所以我們在變長字段長度列表里面是需要指出的。
我們以第一條數據為例,我們知道C1,C2,C4是可變長的數據類型,C3不是,所以我們在這里是需要記錄三列的儲存情況。因為我們采用的是ASCII字符集,所以一個字符我們需要一個字節進行編 碼。那么我來看看儲存數據對應的長度表示:
注意:我們在變長字段長度列表里面進行長度保存的時候是要根據列對應的逆序記性保存,並且只保存值為非NULL的列,所以我們這條數據在變長字段長度列表的表示如下圖所示:
因為數據都較短,所以在變長字段長度列表里面我們使用一個字節對這些信息進行保存,所以我們會產生疑問,在這里保存每一列的信息的時候怎么判斷是使用的一個字節還是兩個字節呢?三個因素:
1>字符集儲存一個字符使用字節M(我們采用的ASCII是1,UTF-8是3,GBK是2)
2>可以儲存的位數W(VARCHAR(W))
3>真實儲存的位數L
規則:
(1)當M * W < 256使用一個字節
(2)當M * W > 256:L小於128使用一個字節,L大於128使用兩個字節。
NULL值列表:
顧名思義這是用來儲存除了NOT NULL,主鍵等關鍵字修飾的列的信息。因為我們的空值列如果進行儲存的話也是需要消耗內存的,所以我們在這里進行記錄,后面的真實數據就是不用進行保存的了。
我們用第二條數據為例:因為C2是不能為空的,所以我們需要記錄的是C1,C2,C3的信息:
我們就來看看這個06數據到底是怎么一回事兒,這個值到底是怎么得到的:
1>如果對應列值為空,那么我們用1進行表示,如果不為空那么我們就用0進行表示,每個列對應一個二進制位。
2>和變長數據長度列表規則一致,我們必須是要使用列的逆序進行表示。
3>如果使用的二進制位不是字節的整數倍,那么我們是需要在高位進行補零操作的。
所以綜合上述三條規則,我們是可以輕易的寫出第二條數據在NULL列表里面的二進制位表示:0000 0110-->對應的十六進制就是:0x06,所以我們到這里就可以知道上面圖片中的06是怎么得到的了。
記錄頭信息:
介紹完了前面的兩個部分接下來就是我們數據額外信息板塊的最后一個部分,記錄頭信息。這部分是5個字節,40個二進制位組成的,那么這40個二進制位分別代表了什么內容:
預留位1 一:沒有進行使用
預留位2 一:沒有進行使用
delete_mask 一:是否被刪除
min_rec_mask 一:該記錄是否為B+樹中非葉子節點的最小記錄
n_owned 四: 當前嘈管理的記錄數
heap_no 十三:當前數據在記錄堆的位置
record type 三:0表示普通記錄,1表示B+樹非節點記錄,2表示最小記錄,3表示最大記錄
next_record 十六:下一條記錄的相對位置
上圖就是我們兩條記錄對應的記錄頭信息,如果我們在這里記不住頭信息的這些概念信息或者是看不懂上圖,沒關系,我們看一下就OK。后邊會繼續詳細的講解。
記錄的真實數據:
記錄的真實數據除了我們插入的那些數據列之外,MySQL數據庫還會幫我們自動生成三個列,也稱之為隱藏列。
注意:行id不是必須有的,是在我們沒有進行主鍵指定的時候才生成的。我們的事務id和回滾指針才是每一條數據都會幫助我們進行添加的。我們不需要關心這三個列的數據添加,因為是MySQL自動幫我們 進行添加的。
我們看看加上真實數據以后,我們添加的兩條記錄的儲存完整格式是什么情況:
注意:
1>在第一條數據中的C3列雖然真實儲存的是 ‘cc’ 但是我們定義的數據類型是char,所以需要進行完整的表示它定義的十個字符空間,剩下的八個用空格字符進行填充。
2>我們在第二條數據中看到的是C3,C4兩個列是沒有在真實數據中進行保存的,因為它們已經在NULL列表里面已經進行了儲存聲明,所以是不需要重復進行儲存的。
3>上邊的數據儲存因為我們采用的是ASCII字符集,當然如果采用其他的字符集是會不一致的。
Redundant:
這個行格式是MySQL5.0之前的版本使用的行格式,非常古老,但是我們還是介紹一下。直接進行和Compact行格式比較:
結構示意圖:
從結構示意圖我們可以看出以下區別:
1>變長字段長度列表變成了-->字段長度偏移列表
2>少了NULL值列表
數據完整信息:
區別:
1>Redundant會把所有列都在字段長度偏移列表進行儲存,包括隱藏列,當然順序依然是逆序。
2>Redundant采用偏移量也就是相鄰兩列的差值進行儲存。
第一列:row_id 六個字節 0x06
第二列:transaction_id 六個字節 0x0c - 0x06 = 0x06
第三列:roll_pointer 七個字節 0x13 - 0x0c = 0x07
。。。。。。。
以此類推我們就可以獲取完整的儲存信息
3>我們在第二條數據可以看到,在真實數據的位置我們是對空值的列進行了相應的用00進行替代保存。在Compact行格式里面我們是不會進行保存的。
記錄頭信息:
預留位1 一:沒有進行使用
預留位2 一:沒有進行使用
delete_mask 一:是否被刪除
min_rec_mask 一:該記錄是否為B+樹中非葉子節點的最小記錄
n_owned 四: 當前嘈管理的記錄數
heap_no 十三:當前數據在記錄堆的位置
n_field 十: 表示記錄中列的數量
1byte_offs_flag 一:標記字段長度偏移列表中的偏移量是使用1字節還是2字節表示的
next_record 十六:下一條記錄的相對位置
區別:
1>少了record_type這個屬性
2>多了n_field
和1byte_offs_flag
這兩個屬性
行溢出數據:
我們在前面提到過一個問題,那就是我們MySQL數據庫是采用頁作為磁盤和內存的中間交換,一頁可以儲存16k的數據,那么如果我們的一條數據超過了這個內存大小,又會發生什么樣的情況呢?其實在這 里我覺得是沒有必要關心多大的數據會發生行溢出的,如果有興趣可以自行百度。在Compact行格式和Redundant中,對於這種特別大的數據的處理方式就是在真實數據的儲存地方留下20個字節的內存用來 儲存指向下一頁的地址。意思就是用幾頁進行儲存,這些頁之間的聯系是使用20個字節內存進行維護。
Dynamic和Compressed:
這兩個行格式和Compact是非常相似的,它們的區別就在於對於上面提出的行溢出的處理。Dynamic是使用所有的真實數據儲存空間進行儲存其它頁面的地址,把所有的真實數據都儲存在其它頁面中。
相比於Dynamic來說,Compressed行格式的處理僅僅是加上了壓縮算法進行壓縮,節省空間。
CHAR類型數據儲存:
我們在前面使用Compact行格式的提到過,比如我們的第一條數據的C3列因為是CHAR的數據類型,所以在變長數據長度列表里面是不進行儲存的。但是在最后需要提出一點就是在那里我們采用的是定長的 字符集ASCII,但是如果我們把字符集換成UTF8的話這一列數據還是會在變長數據長度列表里面進行儲存的。