MySQL行格式的實現(xiàn)
首先我們知道在MySQL中頁是數(shù)據(jù)讀寫的最小單元,默認(rèn)是16KB。頁內(nèi)的記錄會組成一個單鏈表,每條記錄就是一行數(shù)據(jù),行格式決定了一行數(shù)據(jù)是如何進(jìn)行物理存儲的,進(jìn)而影響查詢和DML操作的性能。
? 四種行格式
在InnoDB中,常見的行格式有以下4種:
- compact(緊湊)除了保存字段值外,還會記錄頭信息和記錄變長字段長度列表,以及利用空值列表保存null值。適合處理大量包含可變長度列的數(shù)據(jù),如:varchar、varbinarg、blob和text。(對于可變長序列,在真實數(shù)據(jù)處只會存儲該列的前768字節(jié)的數(shù)據(jù),超出的數(shù)據(jù)分散存儲在其他幾個頁中。然后在真實數(shù)據(jù)處用20個字節(jié)存儲指向溢出頁的地址,從而可以找到剩余數(shù)據(jù)所在的頁。這也就是我們常說的“行溢出”)
- redundant(冗余)是MySQL5.0版本之前InnoDB的行記錄存儲方式,用的比較少。它會將該條記錄中所有列(包括隱藏列)的長度信息都存儲到“字段長度偏移列表”中。
dynamic(動態(tài))是MySQL7.5版本引入,是compact格式的改進(jìn)版,其結(jié)構(gòu)與compact格式大致相同。它與compact格式的不同主要在于行溢出的處理,在真實數(shù)據(jù)處不再去額外記錄一部分?jǐn)?shù)據(jù)了,而是用20個字節(jié)存儲指向溢出頁的地址,所有的數(shù)據(jù)全部在溢出頁中。這種設(shè)計減少了行中額外的開銷,提高了存儲效率和查詢性能。
compressed(壓縮)是MySQL5.1中InnoDB的新特性之一,它在dynamic的基礎(chǔ)上面進(jìn)行壓縮處理,特別是對溢出頁的壓縮處理。在查詢時,會自動解壓數(shù)據(jù)并返回。但compressed格式其實也是用時間換空間,性能并不友好,不推薦在常見的業(yè)務(wù)中使用。
其中,MySQL 5.6 之前默認(rèn)使用 Compact,MySQL 5.7 默認(rèn)使用Dynamic,而redundant 是比較老的數(shù)據(jù)格式,compressed 不能應(yīng)用在系統(tǒng)數(shù)據(jù),所以Compact和Dynamic應(yīng)用較廣泛;
? 如何指定行格式?
我們可以在創(chuàng)建表的時候指定行格式,或者在表創(chuàng)建之后通過 alter 命令更改表的行格式。
// 創(chuàng)建表指定行格式 CREATE TABLE 表名( 建表語句; ) row_format = 行格式名稱; // 修改表的行格式 alter table 表名 row_format = 行格式名稱;
? 詳細(xì)談?wù)刢ompact行格式
如上圖,compact行格式分為四段,分別是:變長字段長度列表+NULL值列表+記錄頭信息+列值。
變長字段長度列表
char和varchar的區(qū)別就是定長和變長,對于變長字段實際存儲的數(shù)據(jù)的長度是不固定的,所以在存儲數(shù)據(jù)的時候也要將數(shù)據(jù)占用的大小存儲起來。也就是,變長列的實際占用字節(jié)數(shù)以逆序方式存儲在變長字段長度列表中。
為什么要逆序存放?
那是因為記錄頭信息指向下一個記錄的指針,指向的是下一條記錄的記錄頭和真實信息之間的位置,這樣使得位置靠前的記錄真實數(shù)據(jù) 和 對應(yīng)的字段長度信息可以同時在一個 CPU緩存中,這樣就可以提高CPU緩存命中率
NULL值列表
表中的某些列可能會存儲NULL值,如果把這些NULL值都放在記錄的真實數(shù)據(jù)中會比較浪費(fèi)空間,所以compact行格式把這些值為NULL的列存儲到NULL值列表中。如果存在允許 null 值的列,則每個列對應(yīng)一個二進(jìn)制位(bit),值為1則代表NULL,0則非空,二進(jìn)制位按照列的順序逆序排序列。另外,NULL值列表必須用整個字節(jié)的位表示(1個字節(jié)8位),如果二進(jìn)制位個數(shù)不足整數(shù)個字節(jié),則高位補(bǔ)0。
記錄頭信息
- 預(yù)留位1【1bit】,沒有使用;
- 預(yù)留位2【1bit】,沒有使用;
- delete_mask【1bit】,標(biāo)記該條記錄是否被刪除。我們執(zhí)行delete刪除記錄時,記錄并不會立刻被物理刪除,而是將這條記錄的delete_mask置為1;
- min_rec_mask【1bit】,標(biāo)識該條記錄是否是某個非葉子節(jié)點頁內(nèi)的最小記錄,幫助 InnoDB 管理索引頁中的記錄順序和邊界;
- n_owned【4bit】,記錄組內(nèi)記錄數(shù)。在頁內(nèi)為了快速二分查找,會分組,只有組內(nèi)最大記錄此字段有值,記錄組內(nèi)記錄數(shù);
- heap_on【13bit】,用于標(biāo)識記錄在頁內(nèi)的邏輯位置。頁內(nèi)的記錄會組成一個單鏈表,heap_on用于標(biāo)識記錄在鏈表中的位置。其中0和1,都是虛擬記錄,表示頁的最小和最大邊界,實際記錄位置從2開始遞增;
- record_type【3bit】,表示當(dāng)前記錄的類型;
- 0 表示普通記錄
- 1 B+樹非葉子節(jié)點記錄
- 2 最小記錄
- 3 最大記錄
- next_record【16bit】,表示下一條記錄的相對位置。
真實數(shù)據(jù)部分
記錄真實數(shù)據(jù)部分,但需要注意的是~除了我們定義的字段外,還有三個隱藏字段,分別是row_id、trx_id、roll_pointer。
- row_id【6Byte】:如果表中沒有指定主鍵且沒有唯一約束列,InnoDB會為記錄添加row_id作為主鍵。如果表中有指定標(biāo)識或者有唯一約束列,那么不會有row_id隱藏列;
- trx_id【6Byte】:該字段是必須有的。事務(wù)id,表示這個數(shù)據(jù)是由哪個事務(wù)生成的;
- roll_pointer【7Byte】:該字段也是必須有的。回滾指針,指向一條undo日志記錄。
到此這篇關(guān)于MySQL行格式的實現(xiàn)的文章就介紹到這了,更多相關(guān)MySQL行格式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MySQL數(shù)據(jù)庫事務(wù)transaction示例講解教程
這篇文章主要為大家介紹了MySQL數(shù)據(jù)庫事務(wù)transaction的示例講解教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2021-10-10MySQL敏感數(shù)據(jù)進(jìn)行加密的幾種方法小結(jié)
本文介紹了在MySQL中對敏感數(shù)據(jù)進(jìn)行加密的幾種方法,每種方法都有其適用場景和特點,可以根據(jù)具體需求選擇合適的方法來保護(hù)數(shù)據(jù)安全,感興趣的可以了解一下2024-11-11MySQL Event Scheduler(事件調(diào)度器)
事件調(diào)度器是在 MySQL 5.1 中新增的另一個特色功能,可以作為定時任務(wù)調(diào)度器,取代部分原先只能用操作系統(tǒng)任務(wù)調(diào)度器才能完成的定時功能。2010-06-06MySQL8.0設(shè)置遠(yuǎn)程訪問權(quán)限的方法
這篇文章主要介紹了MySQL8.0設(shè)置遠(yuǎn)程訪問權(quán)限的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11mysql 卡死 大部分線程長時間處于sending data的狀態(tài)
首先說明一下,這是個無頭的案子,雖然問題貌似解決了,不過到現(xiàn)在我也沒有答案,只是把這個問題拿出來晾晾2008-11-11