MySQL之行格式、頁結(jié)構(gòu)

MySQL之行格式、頁結(jié)構(gòu)

前言

關(guān)于為何要了解MySQL的物理實(shí)現(xiàn):

其實(shí)像B+索引,多版本并發(fā)控制(MVCC)等MySQL常問的技術(shù)知識點(diǎn)都是會(huì)對應(yīng)到具體的物理實(shí)現(xiàn)上,如果不了解MySQL到底怎么存儲數(shù)據(jù),不清楚每個(gè)數(shù)據(jù)行中有什么結(jié)構(gòu),不清楚B+樹中的一個(gè)節(jié)點(diǎn)對應(yīng)什么物理結(jié)構(gòu),又怎么算了解了MySQL

從圖開始理解

以下面這段創(chuàng)建代碼為例:

mysql> CREATE TABLE format_demo (
    ->      c1 VARCHAR(10),
    ->      c2 VARCHAR NOT NULL,
    ->      c3 CHAR(10),
    ->      c4 VARCHAR(10)
    )

那么現(xiàn)在這個(gè)表在我們眼中是這樣的:(插入了兩條數(shù)據(jù))

mysql> SELECT * FROM record_format_demo;
+------+-----+------+------+
| c1   | c2  | c3   | c4   |
+------+-----+------+------+
| aaaa | bbb | cc   | d    |
| eeee | fff | NULL | NULL |
+------+-----+------+------+
2 rows in set (0.00 sec)

mysql>

對應(yīng)在磁盤中表又是怎么樣存儲的呢?

行格式

所謂表的構(gòu)成,實(shí)際就是一行行的數(shù)據(jù),所以在磁盤中表是按行數(shù)據(jù)進(jìn)行存儲的。那么在磁盤中是一整個(gè)表的數(shù)據(jù)都連續(xù)放在一起么?顯然不可能,思考數(shù)據(jù)分頁的方法,MySQL也是按照分頁的方式將一個(gè)表的數(shù)據(jù)拆分開存放。以InnoDB來說:

  • 將數(shù)據(jù)劃分為若干個(gè)頁,以頁作為磁盤和內(nèi)存之間交互的基本單位,InnoDB中頁的大小一般為 16 KB。也就是在一般情況下,一次最少從磁盤中讀取16KB的內(nèi)容到內(nèi)存中,一次最少把內(nèi)存中的16KB內(nèi)容刷新到磁盤中。

關(guān)于的概念先放到這之后再說。

所以現(xiàn)在我們前進(jìn)了一大步,起碼知道磁盤里的表大概長這樣:

磁盤中按頁存儲的數(shù)據(jù).png

再回到行格式上,MySQL中涉及的有這4種:

  1. COMPACT行格式

  2. Redundant行格式

  3. Dynamic行格式

  4. Compressed行格式

主要介紹最重要的COMPACT格式,首先它長這樣:

COMPACT行格式.png

可以看到MySQL除了記錄用戶提供的信息之外還記錄了相當(dāng)?shù)?strong>額外信息,這些信息可以分為3類:

  1. 變長字段長度列表

  2. NULL值列表

  3. 記錄頭信息

簡要說明這些額外信息

變長字段長度列表真實(shí)數(shù)據(jù)中的每個(gè)非空列按其數(shù)據(jù)字節(jié)長度逆序存放起來

變長字段.png

空間分配:長度不是固定的,取決于有多少數(shù)據(jù)

NULL值列表中儲存了所有沒有設(shè)置NOT NULL的字段(列),并用一個(gè)2進(jìn)制位表示該列的NULL狀態(tài),同時(shí)這個(gè)列表也是按列順序的倒序排列的,也就是如果該行數(shù)據(jù)中對應(yīng)第1,3,4個(gè)字段可以為空(沒有設(shè)置NOT NULL)那么NULL值列表就長這樣:

NULL列表.png

需要說明的是,MySQL中無論是定長數(shù)據(jù)還是非定長數(shù)據(jù)都可以設(shè)置對NULL的控制,所以如果該列不為空那么該列數(shù)據(jù)的相關(guān)信息就存在變長字段長度列表中。

空間分配:同時(shí)NULL值列表是按整數(shù)倍字節(jié)分配空間的,不足的位置補(bǔ)上0.

記錄頭信息存儲的都是和行數(shù)據(jù)控制相關(guān)的內(nèi)容:

image.png

空間分配:固定5個(gè)字節(jié)40個(gè)二進(jìn)制位

每段內(nèi)容記錄信息如下:(不做詳細(xì)解釋了,了解大概即可)

名稱 大?。˙it) 描述
預(yù)留位 1 -
預(yù)留位 1 -
delete_mash 1 刪除標(biāo)記位
min_rec_mask 1 B+樹的每層非葉子節(jié)點(diǎn)中的最小記錄都會(huì)添加該標(biāo)記
n_owned 4 表示當(dāng)前記錄擁有的記錄數(shù)
heap_no 13 表示當(dāng)前記錄在記錄堆的位置信息
record_type 3 表示當(dāng)前記錄的類型,0表示普通記錄,1表示B+樹非葉子節(jié)點(diǎn)記錄,2表示最小記錄,3表示最大記錄
next_record 16 表示下一條記錄的相對位置

記錄的真實(shí)數(shù)據(jù)

說完記錄中的額外信息,那么記錄里的真實(shí)數(shù)據(jù)真的只有用戶定義的數(shù)據(jù)么?

顯然不是,MySQL會(huì)自動(dòng)為每個(gè)行數(shù)據(jù)增加一些額外的列,例如DB_ROW_ID,DB_TRX_ID, DB_ROW_PTR分別表示數(shù)據(jù)行的行id, 行的事務(wù)id, 指向下一個(gè)版本數(shù)據(jù)行的指針,其中事務(wù)id指向下一版本的指針實(shí)際都是關(guān)系到MySQL多版本并發(fā)控制的具體實(shí)現(xiàn)。

所以到目前為止,我們已經(jīng)完整了解了一條數(shù)據(jù)行到底長什么樣:

單個(gè)數(shù)據(jù)行.png

關(guān)于定長和非定長類型

VARCHAR()和CHAR()類型的大致區(qū)別,在根據(jù)建表時(shí)設(shè)定的不同字符集下(字符在字符集中對應(yīng)占字節(jié)L),傳入類型中的Maxlen會(huì)限定給CHAR(Maxlen)至少分配ML的空間,而VARCHAR(Maxlen)則是完全根據(jù)數(shù)據(jù)具體字符個(gè)數(shù)*來計(jì)算分配。計(jì)算分配的規(guī)則不在這里說明。

頁結(jié)構(gòu)

下面是頁結(jié)構(gòu)的示意圖:


頁結(jié)構(gòu).png

可以在中間找到我們剛剛討論的行數(shù)據(jù)位于User Records中。

上述內(nèi)容簡述功能如下:

名稱 中文名 占用空間 描述
File Header 文件頭部 38字節(jié) 頁的一些通用信息
Page Header 頁面頭部 56字節(jié) 數(shù)據(jù)頁專有的一些信息
Infimum + Supremum 最小記錄和最大記錄 26字節(jié) 兩個(gè)虛擬的行記錄
User Records 用戶記錄 不確定 實(shí)際存儲的行記錄內(nèi)容
Free Space 空閑空間 不確定 頁中尚未使用的空間
Page Directory 頁面目錄 不確定 頁中的某些記錄的相對位置
File Trailer 文件尾部 8字節(jié) 校驗(yàn)頁是否完整

其中用戶記錄開始是沒有的,當(dāng)插入行數(shù)據(jù)時(shí)會(huì)使用空閑空間,當(dāng)空閑空間裝滿之后就要申請新的

插入行數(shù)據(jù)過程.png

繼續(xù)說明行記錄在頁中怎么組織又需要使用到之前提到的行中的頭信息

大致如下:

  • heap_no屬性表示當(dāng)前記錄在中的位置,用戶插入的數(shù)據(jù)一般從2開始排序,為什么是2呢?

    因?yàn)?0,1中固有的最大最小行記錄,分別指向最大和最小數(shù)據(jù)。這里其實(shí)涉及到了MySQLB+樹的物理實(shí)現(xiàn)過程,簡單來說就是其實(shí)就是B+樹的一個(gè)物理節(jié)點(diǎn),而B+樹中所有節(jié)點(diǎn)是順序排列的,這個(gè)排列的順序就是按照表的唯一主鍵來進(jìn)行的(一般會(huì)設(shè)置一個(gè)與業(yè)務(wù)無關(guān)的邏輯主鍵,并且自增)。所以這兩個(gè)多余的記錄就可以理解成節(jié)點(diǎn)鏈表中的頭尾節(jié)點(diǎn),用于快速遍歷節(jié)點(diǎn)。

  • next_record記錄了從當(dāng)前記錄到下一行數(shù)據(jù)的地址偏移量

那么現(xiàn)在中的行數(shù)據(jù)就是這樣串起來的:

頁中的行數(shù)據(jù).png

這里再提供一個(gè)簡圖說明一個(gè)頁是B+樹中的一個(gè)節(jié)點(diǎn)這句話的意義:

頁和B+樹.png

頁目錄

所以B+樹中的索引是怎么在頁中實(shí)現(xiàn)的呢?這里就涉及到頁中另外一個(gè)關(guān)鍵的內(nèi)容:頁目錄

頁目錄就是MySQL在頁中可以快速檢索數(shù)據(jù)的保障。如果沒有頁目錄,那么定位到了一個(gè)不就只能順序遍歷一次鏈表了。當(dāng)然,頁目錄得以實(shí)現(xiàn)的基礎(chǔ)也是行數(shù)據(jù)是按主鍵進(jìn)行排序存放的。

而目錄的制作過程大致是這樣的:

  1. 將所有正常的記錄(包括最大和最小記錄,不包括標(biāo)記為已刪除的記錄)劃分為幾個(gè)組。

  2. 每個(gè)組的最后一條記錄(也就是組內(nèi)最大的那條記錄)的頭信息中的n_owned屬性表示該記錄擁有多少條記錄,也就是該組內(nèi)共有幾條記錄。

  3. 將每個(gè)組的最后一條記錄的地址偏移量單獨(dú)提取出來按順序存儲到靠近的尾部的地方,這個(gè)地方就是所謂的Page Directory,也就是頁目錄(此時(shí)應(yīng)該返回頭看看頁面各個(gè)部分的圖)。頁面目錄中的這些地址偏移量被稱為(英文名:Slot),所以這個(gè)頁面目錄就是由組成的。

那么最小只有一個(gè)分組的概況如下:


一個(gè)分組.png

可以觀察到最小記錄的n_owned值為1,而最大記錄的n_owned值為5。這關(guān)系到每個(gè)分組是如何設(shè)計(jì)的:

對于最小記錄所在的分組只能有 1 條記錄,最大記錄所在的分組擁有的記錄條數(shù)只能在 1~8 條之間,剩下的分組中記錄的條數(shù)范圍只能在是 4~8 條之間。所以分組是按照下邊的步驟進(jìn)行的:

  • 初始情況下一個(gè)數(shù)據(jù)頁里只有最小記錄和最大記錄兩條記錄,它們分屬于兩個(gè)分組。

  • 之后每插入一條記錄,都會(huì)從頁目錄中找到主鍵值比本記錄的主鍵值大并且差值最小的槽,然后把該槽對應(yīng)的記錄的n_owned值加1,表示本組內(nèi)又添加了一條記錄,直到該組中的記錄數(shù)等于8個(gè)。

  • 在一個(gè)組中的記錄數(shù)等于8個(gè)后再插入一條記錄時(shí),會(huì)將組中的記錄拆分成兩個(gè)組,一個(gè)組中4條記錄,另一個(gè)5條記錄。這個(gè)過程會(huì)在頁目錄中新增一個(gè)來記錄這個(gè)新增分組中最大的那條記錄的偏移量。

所以最終一個(gè)有多個(gè)分組的頁面結(jié)構(gòu)就長這樣:

多個(gè)分組的頁.png

File Header和File Trailer

不出意料Header中保存了這個(gè)中的一些信息,而File Trailer中保存了指向下一個(gè)頁的指針,用于串聯(lián)頁面,也就是B+樹。

全文內(nèi)容總結(jié)自:掘金小冊《MySQL是怎樣運(yùn)行的》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一. Java基礎(chǔ)部分.................................................
    wy_sure閱讀 4,011評論 0 11
  • 1. 字符集和比較規(guī)則 MySQL有4個(gè)級別的字符集和比較規(guī)則,分別是: 服務(wù)器級別 數(shù)據(jù)庫級別 表級別 列級別 ...
    aiwen2017閱讀 648評論 0 0
  • Mysql--InnoDB數(shù)據(jù)頁結(jié)構(gòu) 頁 1.頁是innodb管理存儲空間的基本單位 2.一般大小是16kb 3....
    簡書徐小耳閱讀 2,519評論 1 1
  • 什么是數(shù)據(jù)庫? 數(shù)據(jù)庫是存儲數(shù)據(jù)的集合的單獨(dú)的應(yīng)用程序。每個(gè)數(shù)據(jù)庫具有一個(gè)或多個(gè)不同的API,用于創(chuàng)建,訪問,管理...
    chen_000閱讀 4,138評論 0 19
  • 很久沒畫這樣的圖,生疏了,,,
    遙遙在遠(yuǎn)方閱讀 292評論 1 5

友情鏈接更多精彩內(nèi)容