📢博客主頁:https://blog.csdn.net/2301_779549673
📢博客倉庫:https://gitee.com/JohnKingW/linux_test/tree/master/lesson
📢歡迎點贊 👍 收藏 ?留言 📝 如有錯誤敬請指正!
📢本文由 JohnKi 原創,首發于 CSDN🙉
📢未來很長,值得我們全力奔赴更美好的生活?
文章目錄
- 🏳??🌈一、InnoDB支持的數據行格式都有哪些?
- 1.1 如何查看當前數據庫或表應用了哪種行格式?
- 1.2 如何指定行格式?
- 1.3 DYNAMIC 格式由哪些部分組成?
- 🏳??🌈二、數據區是怎么存儲真實數據的?
- 🏳??🌈三、額外(管理)信息區包含了關于行的哪些信息?
- 🏳??🌈四、頭信息區域包含了哪些信息?
- 4.1 刪除一行記錄時在InnoDB內部執行了哪些操作?
- 🏳??🌈五、NuIl列表有啥作用? 列表中的值是什么?
- 🏳??🌈六、變長字段列表有啥作用?列表中的值是什么?
- 6.1 如何記錄變長字段的實際長度?
- 6.2 讀取長度時如何處理粘包問題?
- 🏳??🌈七、其他的行格式與 DYNAMIC 有什么區別?
- 7.1 REDUNDANT 冗余格式
- 7.2 COMPRESSED 壓縮格式
- 7.3 COMPACT 緊湊格式
- 👥總結
真實的數據在表空間以數據行的形式存儲,也就是說每一條數據都對應著表中的一行,數據行在頁中的位置如下圖所示:
🏳??🌈一、InnoDB支持的數據行格式都有哪些?
InnoDB
支持四種行格式,分別是: REDUNDANT 冗余格式
,COMPACT 緊湊格式
,DYNAMIC 動態格式
和 COMPRESSED 壓縮格式
默認是 DYNAMIC
格式。
1.1 如何查看當前數據庫或表應用了哪種行格式?
# 查看系統變量中設置的?格式
mysql> SHOW VARIABLES LIKE 'innodb_default_row_format';
+---------------------------+---------+
| Variable_name | Value |
+---------------------------+---------+
| innodb_default_row_format | dynamic |
+---------------------------+---------+
1 row in set (0.00 sec)
# 使?SHOW table STATUS查看數據庫中的所有表
mysql> SHOW TABLE STATUS IN test_db\G
*************************** 1. row ***************************Name: classesEngine: InnoDBVersion: 10Row_format: Dynamic # 指定數據庫使?的?格式Rows: 3Avg_row_length: 5461Data_length: 16384
Max_data_length: 0Index_length: 0Data_free: 0Auto_increment: 4Create_time: 2025-05-02 19:14:14Update_time: NULLCheck_time: NULLCollation: utf8mb4_general_ciChecksum: NULLCreate_options: row_format=DYNAMICComment:
*************************** 2. row ***************************Name: courseEngine: InnoDBVersion: 10Row_format: DynamicRows: 6Avg_row_length: 2730Data_length: 16384
Max_data_length: 0Index_length: 0Data_free: 0Auto_increment: 7Create_time: 2025-05-02 19:14:14Update_time: NULLCheck_time: NULLCollation: utf8mb4_general_ciChecksum: NULLCreate_options: row_format=DYNAMICComment:
*************************** 3. row ***************************Name: scoreEngine: InnoDBVersion: 10Row_format: DynamicRows: 15Avg_row_length: 1092Data_length: 16384
Max_data_length: 0Index_length: 0Data_free: 0Auto_increment: NULLCreate_time: 2025-05-02 19:14:14Update_time: NULLCheck_time: NULLCollation: utf8mb4_general_ciChecksum: NULLCreate_options: row_format=DYNAMICComment:
# ... 省略
16 rows in set (0.06 sec)
1.2 如何指定行格式?
- 可以通過
全局變量
設置行格式 - 也可以在創建表中通過
ROW_FORMAT
子句指定行格式:
# 通過全局變量設置
SET GLOBAL innodb_default_row_format=DYNAMIC;# 在創建表時明確的指定?格式
CREATE TABLE t1 (c1 INT) ROW_FORMAT=DYNAMIC;
1.3 DYNAMIC 格式由哪些部分組成?
一個 DYNAMIC
格式的數據行會被分為兩部分
- 一部分是存儲
真實數據
的區域 - 一部分是存儲
額外信息
的區域
在頁結構的小節已經對行做了簡單介紹,下面來詳細講解一下行的組成結構
🏳??🌈二、數據區是怎么存儲真實數據的?
數據區在數據行中的位置如下圖所示:
從分隔線向右第一個字段存儲真實數據的主鍵值,對于 主鍵值
有以下幾種情況:
- 如果表中定義了
主鍵
,則直接存儲主鍵
的值;。 - 如果是
復合主鍵
會根據列定義的順序
依次排列在這里; - 如果
沒有
主鍵,會優先使用第一個不允許為 NULL 的 UNIQUE 唯一列
作為主鍵; - 如果
既沒有主鍵也沒有唯一鍵
,那么InnoDB會構建一個6字節的字段DB_ROW_ID
作為行的唯一標識,存儲在真實數據的頭部
緊接著是在事務運行中兩個非常重要的固定字段
- 6字節的事務ID字段
DB_TXID
,記錄創建或最后一次修改該記錄的事務ID - 7字節的回滾指針字段
DB_ROLL_PTR
,如果在事務中這條記錄被修改,指向這條記錄的上一個版本
接下來就是除了 主鍵
和 值為NULL
的列之外,其他列的真實數據,按照順序從左到右依次排列
至于為什么不存儲NULL值,原因很簡單,就是為了節少空間,所有允許為NULL的列都會在行額外信息區的NULL值列表中進行標識 ,后面我們會詳細詳解,以上就是數據行對真實數據的存儲方式。
🏳??🌈三、額外(管理)信息區包含了關于行的哪些信息?
額外信息區從右向左分別為: 頭信息
,NuI值列表
,變長字段列表
。
🏳??🌈四、頭信息區域包含了哪些信息?
- 下一行地址偏移量:
next_record
占16bit,通過這個信息將所有的行鏈接成一個單向鏈表 - 行類型:
record_type
占3bit,包括四種類型:- 0: 普通數據行
- 1: 索引目錄行
- 2: 頁內最小行
infimun
- 3: 頁內最大行
supremun
- 行在整個頁中的位置:
heap_no
占13bit; - 分組的行數:
n_owned
占4bit,只在該行是分組最后一行才有值,這樣就可以快速查詢行數,而不用一條條的累加了 - B+樹索引樹每層最小值標記:
min_rec_flag
占lbit,如果當前行的類型是目錄行也就是record_type=1
,同時也是B+索引樹某層的最小值,則會置為1,會在索引查詢時用到,后面我們講索引時再介紹 - 刪除標記:
delete_mask
占1bit
,從頁中刪除數據行時,并不會直接移除,而是修改這個刪除標記為 - 預留區: 占2bit
4.1 刪除一行記錄時在InnoDB內部執行了哪些操作?
從頁中刪除數據行時,并不會直接移除,而是修改 delete_mask
這個刪除標記為1,并將 next_record
改為0,同時將 上一行的 next_record
指向后續的行,從而把該行從鏈表中斷開
如果執行事務提交后,則將這行的 next_record
指向一個被稱為垃圾鏈表的區域,這個鏈表會被用在事務回滾中,后續在事務中詳細介紹
🏳??🌈五、NuIl列表有啥作用? 列表中的值是什么?
- 頭信息區再向右就是
NULL值列表的可變區域
,用來存儲數據行中所有列允許為Null的值從而節省空間,具體的實現方式是,用1BIT的大小來表示行中某一列是否為空,這樣空列就不需要記錄在真實數據區域中了 - 為每個沒有定義
NOT NULL
約束也就是可以為NULL的列在NULL值列表中都安排了一個bit位,按列序號從小到大的順序從右至左依序安排,這就是常說的逆序排列,NULL值列表最小1字節即8bit,如果沒有那么多可以為NULL的列,則會用0補滿8bit,如果為值為NULL的列超過8個,則新開辟1字節的空間,依此類推: - 如果某列為空,則NULL值列表中對應的bit設置為1,這樣只用了一bit就存儲了NULL列,非常節省空間
🏳??🌈六、變長字段列表有啥作用?列表中的值是什么?
查看編碼集所占的字節數
mysql> show charset;
+----------+---------------------------------+---------------------+--------+
| Charset | Description | Default collation | Maxlen |
+----------+---------------------------------+---------------------+--------+
| armscii8 | ARMSCII-8 Armenian | armscii8_general_ci | 1 |
| ascii | US ASCII | ascii_general_ci | 1 |
| big5 | Big5 Traditional Chinese | big5_chinese_ci | 2 |
| binary | Binary pseudo charset | binary | 1 |
| cp1250 | Windows Central European | cp1250_general_ci | 1 |
| cp1251 | Windows Cyrillic | cp1251_general_ci | 1 |
| cp1256 | Windows Arabic | cp1256_general_ci | 1 |
| cp1257 | Windows Baltic | cp1257_general_ci | 1 |
| cp850 | DOS West European | cp850_general_ci | 1 |
| cp852 | DOS Central European | cp852_general_ci | 1 |
| cp866 | DOS Russian | cp866_general_ci | 1 |
| cp932 | SJIS for Windows Japanese | cp932_japanese_ci | 2 |
| dec8 | DEC West European | dec8_swedish_ci | 1 |
| eucjpms | UJIS for Windows Japanese | eucjpms_japanese_ci | 3 |
| euckr | EUC-KR Korean | euckr_korean_ci | 2 |
| gb18030 | China National Standard GB18030 | gb18030_chinese_ci | 4 |
| gb2312 | GB2312 Simplified Chinese | gb2312_chinese_ci | 2 |
| gbk | GBK Simplified Chinese | gbk_chinese_ci | 2 |
| geostd8 | GEOSTD8 Georgian | geostd8_general_ci | 1 |
| greek | ISO 8859-7 Greek | greek_general_ci | 1 |
| hebrew | ISO 8859-8 Hebrew | hebrew_general_ci | 1 |
| hp8 | HP West European | hp8_english_ci | 1 |
| keybcs2 | DOS Kamenicky Czech-Slovak | keybcs2_general_ci | 1 |
| koi8r | KOI8-R Relcom Russian | koi8r_general_ci | 1 |
| koi8u | KOI8-U Ukrainian | koi8u_general_ci | 1 |
| latin1 | cp1252 West European | latin1_swedish_ci | 1 |
| latin2 | ISO 8859-2 Central European | latin2_general_ci | 1 |
| latin5 | ISO 8859-9 Turkish | latin5_turkish_ci | 1 |
| latin7 | ISO 8859-13 Baltic | latin7_general_ci | 1 |
| macce | Mac Central European | macce_general_ci | 1 |
| macroman | Mac West European | macroman_general_ci | 1 |
| sjis | Shift-JIS Japanese | sjis_japanese_ci | 2 |
| swe7 | 7bit Swedish | swe7_swedish_ci | 1 |
| tis620 | TIS620 Thai | tis620_thai_ci | 1 |
| ucs2 | UCS-2 Unicode | ucs2_general_ci | 2 |
| ujis | EUC-JP Japanese | ujis_japanese_ci | 3 |
| utf16 | UTF-16 Unicode | utf16_general_ci | 4 |
| utf16le | UTF-16LE Unicode | utf16le_general_ci | 4 |
| utf32 | UTF-32 Unicode | utf32_general_ci | 4 |
| utf8mb3 | UTF-8 Unicode | utf8mb3_general_ci | 3 |
| utf8mb4 | UTF-8 Unicode | utf8mb4_0900_ai_ci | 4 |
+----------+---------------------------------+---------------------+--------+
41 rows in set (0.00 sec)
以下是?個建表的SQL語句
mysql> CREATE TABLE test_student (-> `id` bigint NOT NULL AUTO_INCREMENT,-> `sn` char(10) NOT NULL,-> `name` varchar(50) NOT NULL,-> `age` int NOT NULL,-> `mail` varchar(100) NOT NULL,-> `remark` varchar(255) NULL,-> PRIMARY KEY (`id`)-> );
Query OK, 0 rows affected (0.09 sec)
- 行結構的最左側是變長字段列表,也叫可變字段長度列表,在這個列表中記錄了數據行中所有變長字段的實際長度,這樣做的目的,是為了在真實數據區域,可以根據列的長度進行列與列之間的分割;
- 需要記錄的變長字段類型常見的有varchar、varbinary、text、blob,以及當使用了例如utf-8、gbk等變長字符集的char類型,當char類型的字節數可能超過768個字節時,比如使用utf8mb4字符集時定義了char(255),這個字段的最大字節數是4*255=1020
- 每個變長字段分配1~2個字節來存放這些字段的真實大小,放置順序也是按表中字段的順序從右至左逆序排列;
mysql> CREATE TABLE test_varchar (-> `id` bigint NOT NULL AUTO_INCREMENT,-> `name` varchar(20000) NULL,-> PRIMARY KEY (`id`)-> );
ERROR 1074 (42000): Column length too big for column 'name' (max = 16383); use BLOB or TEXT instead
- 2個字節最大可以表示65535個字節,按照最大長度字符串,比如 utf8mb4,一個字符占用最多4個字節計算,2個字節最多可以表示65535/4=16383個字符,列數據類型varchar的長度上限16383就是根據這個計算來的;
- 需要特別說明的是,如果text、blob存儲的內容過大,一個頁已經不夠放了,就會把這個列放入一個叫"溢出頁"的獨立空間中,在這個數據行對應的真實數據處,只使用20個字節來標記這個溢出頁的位置信息
6.1 如何記錄變長字段的實際長度?
不同的字符集在處理字符對應的最大字節長度不同,以如 ascii
最大1個字節, utf8mb3
最大3個字節,utf8mb4
最大4個字節,如下所示
| ascii | US ASCII | ascii_general_ci | 1 |
| utf8mb3 | UTF-8 Unicode | utf8mb3_general_ci | 3 |
| utf8mb4 | UTF-8 Unicode | utf8mb4_0900_ai_ci | 4 |
當使用 varchar(M )
指定一個字段的最大字符數時,該字段真實使用的字節數與建表時指定的字符集有關,如果指定的字符集單個字符最大占 w個字節,從理論上講,該列最多使用的字節數 M *如果 M *W<= 255 則用一個字節記錄這個變長字段的長度就足夠了
如果 M *W> 255
可能分為兩種情況,假設當前變長字段實現占用了L個字節:
6.2 讀取長度時如何處理粘包問題?
- 也就是說在讀取變長字段長度時,如何確定讀取一個字節還是兩個字節?
- 在任何時候都是先讀一個字節,然后判斷這個字節的高位是否為0,如果是0則表示當前用一個字節表示長度,如果是1則表示當前用兩個字節表示長度
- 為1時再讀一個字節,然后合并在一起進行解析得到該字段真實的使用的字節數,而且第二個BIT位表示是否使用溢出頁
默認數據頁大小為16KB,數據頁中一個數據行的大小最大為8KB
🏳??🌈七、其他的行格式與 DYNAMIC 有什么區別?
7.1 REDUNDANT 冗余格式
已被淘汰,之所以存在是為了與舊版本 MySQL兼容,不建議使用,這里不再討論。
7.2 COMPRESSED 壓縮格式
行結構與 DYNAMIC 完全相同,只是會對數據進行壓縮,以減少對空間的占用。
7.3 COMPACT 緊湊格式
在結構上與 DYNAMIC 相同,只是對超長字段的處理上有些區別,它不會把所有超長數據都放在溢出頁中,而是會在本行中保留前768個字節的數據,多出的部分放在溢出頁中,溢出頁的地址額外用20個字節表示,那么在本行的列中就會占用768+20個字節。
👥總結
本篇博文對 【MySQL】行結構詳解:InnoDb支持格式、如何存儲、頭信息區域、Null列表、變長字段以及與其他格式的對比 做了一個較為詳細的介紹,不知道對你有沒有幫助呢
覺得博主寫得還不錯的三連支持下吧!會繼續努力的~