在開文我先說明一下,接下來的數據庫知識文章都是在微信公衆號「咱們都是小青蛙」學習而後在經過本身的理解進行書寫的。有興趣的朋友能夠去關注這個微信公衆號。話很少說,咱們在平常使用數據庫進行數據持 久化的時候有沒有想過咱們的數據在數據庫中是什麼樣的儲存結構,咱們可能想的最多的是怎樣進行SQL的調優,可是對於數據庫都不熟悉能作到調優設計麼?答案顯然是不能!!因此咱們在這裏開始數據庫的第一 篇文章。數據庫的記錄儲存結構。算法
咱們可能有不少熟悉的數據庫儲存引擎,好比說Inoodb,MyISAM,Memory。每一種儲存引擎對於數據的持久化多是不一樣的,好比說咱們的Memory儲存引擎的數據都是不會寫進磁盤的,全部的數據是保存在內存 中的,也就意味着若是咱們的服務器進行重啓之後,數據是不會被進行保存的。固然,由於MySQL數據庫默認的儲存引擎是使用的Inoodb,因此咱們在這裏是須要重點介紹這個儲存引擎的。數據庫
簡介:服務器
Inoodb儲存引擎是把數據儲存在磁盤裏面的儲存引擎,它在內存和磁盤的交互中使用的是頁這個數據單位。咱們都知道一個事情就是咱們在對磁盤進行訪問的時候速度是很是慢的,因此咱們確定是不能接受一 條數據一條數據的進行取用。全部數據劃分爲若干頁,一個數據頁是能夠保存16kb的數據,也就是說咱們每次在進行數據訪問的時候是一次性的16kb數據。微信
行格式:學習
知道了數據庫中咱們數據的大概儲存方式,那麼接下來咱們須要作的就是學習一條數據在咱們的數據庫中是什麼樣的一個結構存在。咱們把數據庫儲存數據的格式稱之爲行格式或者是記錄格式,咱們如今所使用 的行格式有Compact,Redundant,Dynamic,Compressed。固然隨着時間的遷移咱們是會有更多的行格式出現。編碼
Compact:spa
建立語法:咱們會在bysj數據庫中建立一個表,test,方便咱們接下來對於儲存結構的演示。以下所示,設置行格式咱們直接使用ROW_FORMAT=行格式名稱 。咱們同時也對編碼格式進行了設置爲ASCII,這個 編碼格式只能是儲存空格,字母,標點符號,不可見字符,數字。因此漢字是不能夠儲存進來的!設計
接下來咱們插入兩條數據:3d
結構示意圖:以下圖所示咱們能夠看到的是分爲兩個大部分,第一部分就是記錄額外信息,第二部分就是記錄真實數據,接下來咱們對這兩個部分進行詳細的描述:指針
變長字段長度列表:
這個部分記錄的是可變長字段的信息,好比說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的話這一列數據仍是會在變長數據長度列表裏面進行儲存的。