MySQL之InnoDB存儲引擎中的頁用法解讀
1、背景
mysql中存儲數(shù)據(jù)是存儲引擎干的事,存儲引擎存儲數(shù)據(jù)的基本單位是頁,我們往數(shù)據(jù)庫插入表中的一條條記錄就是存儲在頁上的,今天我們就來熟悉一下頁上面有哪些內(nèi)容。
2、頁的組成
頁由7部分組成,先大概字面意思理解一下,后面再各部分詳細(xì)講解,組成圖如下:

表格解釋如下:
| 名稱 | 字節(jié)大小 | 描述 |
|---|---|---|
| 文件頭部 | 38 | 頁的通用信息 |
| 頁頭部 | 56 | 頁的專有信息 |
| 最小記錄和最大記錄 | 26 | 2個固定的行記錄 |
| 行記錄 | 不固定 | 存放用戶數(shù)據(jù) |
| 空閑空間 | 不固定 | 未使用存放用戶數(shù)據(jù)的空間 |
| 頁目錄 | 不固定 | 存放槽 |
| 文件尾部 | 8 | 校驗頁面是否完整 |
3、各部分講解
【1】文件頭部
文件頭部38個字節(jié)由8個部分組成,看表理解:
| 名稱 | 字節(jié)大小 | 描述 |
|---|---|---|
| FIL_PAGE_SPACE_OR_CHKSUM | 4 | 表空間id或校驗和 |
| FIL_PAGE_OFFSET | 4 | 頁號,當(dāng)前頁的唯一標(biāo)識 |
| FIL_PAGE_PREV | 4 | 上一個頁號唯一標(biāo)識 |
| FIL_PAGE_NEXT | 4 | 下一個頁號唯一標(biāo)識 |
| FIL_PAGE_LSN | 8 | 該頁最后一次修改對應(yīng)的redo日志序號 |
| FIL_PAGE_TYPE | 2 | 頁類型 |
| FIL_PAGE_FILE_FLUSH_LSH | 8 | 該頁被刷到磁盤的redo日志序號 |
| FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID | 4 | 歸檔日志編號或頁屬于哪個表空間 |
1、本文是在FIL_PAGE_TYPE為數(shù)據(jù)頁的基礎(chǔ)上進(jìn)行講解
2、FIL_PAGE_SPACE_OR_CHKSUM對于數(shù)據(jù)頁的類型含義為校驗和,用來和文件尾部的校驗和進(jìn)行對比
【2】頁頭部
頁頭部的56個字節(jié)由14個部分組成,看表理解:
| 名稱 | 字節(jié)大小 | 描述 |
|---|---|---|
| PAGE_N_DIR_SLOTS | 2 | 頁目錄中存儲槽的數(shù)量 |
| PAGE_HEAP_TOP | 2 | 頁中空閑空間地址 |
| PAGE_N_HEAP | 2 | 未標(biāo)記刪除用戶記錄 + 已標(biāo)記刪除用戶記錄 + 最小記錄 + 最大記錄 |
| PAGE_FREE | 2 | 第1條標(biāo)記為已刪除的用戶記錄地址 |
| PAGE_GARBAGE | 2 | 所有標(biāo)記為已刪除記錄占用的字節(jié)數(shù) |
| PAGE_LAST_INSERT | 2 | 最后一條用戶記錄插入位置 |
| PAGE_DIRECTION | 2 | 最后一條用戶記錄插入方向,主鍵/unique健/隱藏列唯一鍵比上一條大就為右,反之為左 |
| PAGE_N_RECS | 2 | 未標(biāo)記為刪除的用戶記錄數(shù) |
| PAGE_MAX_TRX_ID | 8 | 修改當(dāng)前頁的最新事務(wù)id |
| PAGE_LEVEL | 2 | 當(dāng)前頁在B+樹中的層級 |
| PAGE_INDEX_ID | 8 | 當(dāng)前頁屬于的索引ID |
| PAGE_BTR_SEG_LEAF | 10 | B+樹葉子段的頭部信息 |
| PAGE_BTR_SEG_TOP | 10 | B+樹非葉子段的頭部信息 |
【3】最小記錄和最大記錄
最小記錄和最大記錄2條記錄是頁中固定就存在的,最小記錄和最大記錄都是由5字節(jié)頭部信息+8字節(jié)數(shù)據(jù)組成,所以它們總共占用空間26字節(jié),最小記錄數(shù)據(jù)部分固定為’infimum’,最大記錄數(shù)據(jù)部分固定為’supremum’, 在講它之前我們先了解一下Compact行格式中頭部信息用5字節(jié)來存儲的行記錄信息,其組成部分如下:
| 名稱 | 位大小 | 描述 |
|---|---|---|
| 預(yù)留位 | 1 | 未使用 |
| 預(yù)留位 | 1 | 未使用 |
| delete_mask | 1 | 0-記錄未刪除,1-記錄已刪除 |
| min_rec_mask | 1 | B+樹非葉子節(jié)點中的最小記錄 |
| n_owned | 4 | 當(dāng)前記錄在記錄組里擁有記錄數(shù) |
| heap_no | 13 | 當(dāng)前記錄在記錄堆中的位置 |
| record_type | 3 | 記錄類型,0-普通記錄,1-B+數(shù)非葉子節(jié)點記錄,2-最小記錄,3-最大記錄 |
| next_record | 16 | 指向下一條未被刪除的記錄 |
當(dāng)頁中沒有任何用戶記錄時,最小記錄和最大記錄可以如圖表示:

最小記錄中的n_owned為1表示以最小記錄結(jié)尾的分組記錄中只有一條記錄,這條記錄就是最小記錄本身,最小記錄的next_record為13代表最小記錄數(shù)據(jù)部分與下一條數(shù)據(jù)部分的偏移量,所以上圖箭頭指向最大記錄的固定數(shù)據(jù)部分,13字節(jié)大小是這樣計算的: 最小記錄固定數(shù)據(jù)部分(8) + 最大記錄頭部信息(5)
next_record之所以指向下一條記錄的數(shù)據(jù)部分是因為:對于用戶記錄,指向位置往左的頭部信息中還有逆序的長度列表、逆序的是否為NULL列表,往右就是數(shù)據(jù)部分。
最大記錄中的n_owned為1表示以最大記錄結(jié)尾的分組記錄中只有一條記錄,這條記錄就是最大記錄本身,最大記錄的next_record為0代表沒有下一條記錄,它就是最后一條記錄。
n_owned數(shù)有這樣的定義:
- 1、最小記錄所在分組記錄數(shù)最多只能有1條。
- 2、最大記錄所在分組記錄數(shù)范圍為1~8條。
- 3、用戶記錄所在分組記錄數(shù)范圍為4~8條。
【4】行記錄
行記錄講解需要根據(jù)數(shù)據(jù)來進(jìn)行理解了,我們先創(chuàng)建一張表如下:
CREATE TABLE test
(
id INT AUTO_INCREMENT PRIMARY KEY,
str VARCHAR(255) NOT NULL DEFAULT ''
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
插入3條記錄:
INSERT INTO test (str)
VALUES ('AAA'),
('BBB'),
('CCC');
查看記錄:
mysql [xxx]> select * from test; +----+-----+ | id | str | +----+-----+ | 1 | AAA | | 2 | BBB | | 3 | CCC | +----+-----+ 3 rows in set (0.001 sec)
此時插入的3條記錄和最小最大記錄可以如圖表示:

可以看到所有記錄通過nex_record的偏移量組成了一個鏈表,刪除其中某條記錄,就會把該條記錄的delete_mask設(shè)置為1,next_record設(shè)置為0,但是記錄依然存在,只是標(biāo)記為了刪除,這是為了后面插入新數(shù)據(jù)時可以對這部分空間進(jìn)行復(fù)用,如圖:

因為是第1條標(biāo)記刪除的記錄,也會在頁頭部記錄這條記錄的地址,如果有多條刪除記錄,它們之間也會組成一個鏈表。
【5】空閑空間
空閑空間就是給行記錄使用的,行記錄空間增加,空閑空間就減少。
【6】頁目錄
頁目錄就是用來存儲槽的,槽就是一組用戶記錄中,相對頁起始位置偏移量最大的一條記錄, 這條記錄數(shù)據(jù)部分相對頁起始位置的偏移量就作為槽,槽對應(yīng)的記錄中的n_owned屬性就代表這條記錄對應(yīng)的用戶組內(nèi)有多少條記錄,槽與記錄關(guān)系可以如圖表示:

最大記錄看起來雖然是在最后面,起始它是和最小記錄挨著的,所以后面記錄增加導(dǎo)致分組產(chǎn)生新增槽時相對頁起始位置的偏移量要比槽1大。
在講最小記錄和最大記錄時講過分組記錄的約束,根據(jù)這個約束行記錄分組產(chǎn)生規(guī)則如下:
1、新增記錄放到比插入記錄主鍵值大且主鍵值相隔最近的行記錄對應(yīng)的槽里,并將當(dāng)前槽對應(yīng)行記錄的n_owned屬性+1,表示該槽對應(yīng)的記錄組的記錄多了1個。
2、當(dāng)記錄組的記錄數(shù)變?yōu)?個時,會將這9條記錄分為2個組,一組4個一組5個,對于4條記錄的分組會將里面相對頁起始位置最大的值的行記錄的數(shù)據(jù)部分偏移量(相對頁起始位置)設(shè)置為新的槽。
查找記錄的規(guī)則如下:
1、通過二分法找到對應(yīng)的槽。
2、根據(jù)行記錄的next_record屬性找到對應(yīng)的行。
【7】文件尾部
文件尾部大小為8個字節(jié),前4個字節(jié)代表頁的校驗和,和文件頭部的校驗和相等就代表是一個完成的頁,后4個字節(jié)代表最后一次修改當(dāng)前頁是對應(yīng)的redo日志序號,這個以后再講。
4、總結(jié)
本篇文章講解了數(shù)據(jù)頁的組成,頁是存儲數(shù)據(jù)的基本單位;每個頁的文件頭部都會有一個上下頁號,組成了一個雙向鏈表;頁中每條行記錄的next_record組成一個單項鏈表;InnoDB會把多條記錄分為一個組,組里的最大記錄的數(shù)據(jù)部分相對于頁面開頭的偏移量就設(shè)置為槽,查找記錄時通過二分法查找所在的槽,再通過行記錄的next_record屬性找到對應(yīng)的記錄。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
MySql設(shè)置指定用戶數(shù)據(jù)庫查看查詢權(quán)限
這篇文章主要介紹了MySql設(shè)置指定用戶數(shù)據(jù)庫查看查詢權(quán)限,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-10-10
關(guān)于Mysql5.7及8.0版本索引失效情況匯總
這篇文章主要介紹了關(guān)于Mysql5.7及8.0版本索引失效情況匯總,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08
MySQL索引背后的數(shù)據(jù)結(jié)構(gòu)及算法原理詳解
本文以MySQL數(shù)據(jù)庫為研究對象,討論與數(shù)據(jù)庫索引相關(guān)的一些話題。特別需要說明的是,MySQL支持諸多存儲引擎,而各種存儲引擎對索引的支持也各不相同,因此MySQL數(shù)據(jù)庫支持多種索引類型,如BTree索引,哈希索引,全文索引等等2016-12-12
MySQL字符串日期格式轉(zhuǎn)換的幾種常用方法例子解析
這篇文章主要給大家介紹了MySQL字符串日期格式轉(zhuǎn)換的幾種常用方法例子解析,MySQL提供了多種函數(shù)來處理字符串日期格式的轉(zhuǎn)換,這些函數(shù)可以幫助用戶在不同的日期格式之間進(jìn)行轉(zhuǎn)換,以及進(jìn)行日期的加減和時間戳的轉(zhuǎn)換操作,需要的朋友可以參考下2024-11-11

