Mysql?InnoDB引擎中的數(shù)據(jù)頁結(jié)構(gòu)詳解
Mysql InnoDB引擎數(shù)據(jù)頁結(jié)構(gòu)
InnoDB 是 mysql 的默認(rèn)引擎,也是我們最常用的,所以基于 InnoDB,學(xué)習(xí)頁結(jié)構(gòu)。而學(xué)習(xí)頁結(jié)構(gòu),是為了更好的學(xué)習(xí)索引。
一、頁的簡介
頁是 InnoDB 管理存儲空間的基本單位,一個頁的大小一般是 16kb。
為了達(dá)成不同的目的,作者設(shè)計(jì)了多種類型的頁,比如:
- 存放表空間頭部信息的頁
- 存放 change buffer 信息的頁
- 存放 inode 信息的頁
- 存放 undo 日志信息的頁
- ... ...
然而我們最關(guān)心的,還是那些存放進(jìn)表中那些數(shù)據(jù)記錄是在哪種頁上,官方稱這種存放記錄的頁為索引(INDEX)頁,但是為了便于理解,本篇暫把它稱為數(shù)據(jù)頁。
二、數(shù)據(jù)頁的結(jié)構(gòu)
這數(shù)據(jù)頁也有 16kb 的存儲空間,可以大致劃分為 7 個部分。
從結(jié)構(gòu)圖中可以看到,有些部分的占用字節(jié)數(shù)是確定的,有的是不確定的。我們最關(guān)心的用戶存儲的記錄,在 User Records部分。
不過,在一開始生成頁的時(shí)候,并沒有 User Records 部分。當(dāng)有新的記錄插入時(shí),就會從 Free Space部分申請一個記錄大小的空間,然后劃分到 User Records 部分,直到 Free Space 全部被 User Records 替代,表示這個頁已經(jīng)用完。如果還有新的記錄插入,需要申請新的頁。
我覺得這里可以把這個數(shù)據(jù)頁當(dāng)作是書本的頁,書頁上的內(nèi)容通常是一行行的呈現(xiàn),當(dāng)整個頁都用完了,就得翻到下一頁(新頁)去繼續(xù)寫了。
三、記錄在頁中的存儲結(jié)構(gòu)
那么,User Records 部分里的這些記錄,是如何管理的呢?
先來建一張表:
CREATE TABLE pingguo_demo( c1 INT, c2 INT, c3 VARCHAR(10000), PRIMARY KEY (c1) ) CHARSET = ASCII ROW_FORMAT = COMPACT;
這里的指定使用行格式為 COMPACT(引擎中還存在其他的行格式),暫且知道 COMPACT 即可。
當(dāng)我們在數(shù)據(jù)庫的插入了一條記錄后,其實(shí)背后的行格式是這樣的:
注意這里橙色標(biāo)識的記錄頭信息,它又包含了很多重要信息:
- 預(yù)留位1:占用 1 比特,沒有使用。
- 預(yù)留位2:占用 1 比特,沒有使用。
- deleted_flag:占用 1 比特,標(biāo)記該記錄是否被刪除。
- min_rec_flag:占用 1 比特,在 B+ 樹(后面索引會講到)中每層非 葉子節(jié)點(diǎn)中的最小的目錄項(xiàng),都會添加此標(biāo)記。
- n_owned:一個頁面中的記錄被分為若干個組,每個組里有一個記錄是“大哥”,其他記錄都是“小弟”。而這位“大哥”記錄的 n_owned 就是所在組的所有記錄條數(shù),而小弟們的 n_owned 都是 0
- heap_no:占用 13 比特,表示當(dāng)前記錄在頁面堆中的相對位置。
- record_type:占用 3 比特,表示當(dāng)前記錄的類型,0是普通記錄,1是 B+樹非葉節(jié)點(diǎn)的目錄項(xiàng)記錄,2是 Infimum 記錄,3是 Suprememum 記錄。
- next_record:占用 16 比特,表示下一條記錄的相對位置。
四、記錄頭信息
現(xiàn)在,向上面新建的表中插入 4 條記錄:
INSERT INTO pingguo_demo VALUES (1, 100, 'aaaa'), (2, 200, 'bbbb'), (3, 300, 'cccc'), (4, 400, 'dddd');
那么,對應(yīng)這4條記錄的行格式應(yīng)該為:
注意,這里為了便于記憶,作了簡化。另外,記錄中的信息實(shí)際是二進(jìn)制位數(shù)據(jù),這里為了理解寫的是十進(jìn)制。而且,各條記錄在 User Records 中存儲是沒有空隙的,這里抽象表示。
1. deleted_flag
這個屬性用來標(biāo)記當(dāng)前記錄是否被刪除,1 表示被刪除,0 表示沒有被刪除。
嗯?我表里刪除了數(shù)據(jù)居然還在頁里。
是的,你以為被刪除了,其實(shí)還在磁盤上。為什么呢?
因?yàn)槿绻诖疟P上移除這些記錄,還要再重新排列其他記錄,會帶來性能消耗,所以只打了一個刪除的標(biāo)記。
然后,所有的刪除的記錄會組成一個垃圾鏈表。而記錄在這個鏈表中所占用的空間稱為可重用空間,當(dāng)后面有新記錄插入到表中,它們就可能覆蓋掉這些空間。
2. min_rec_flag
在 B+ 樹中每層非葉子節(jié)點(diǎn)中的最小的目錄項(xiàng),都會添加此標(biāo)記。這里說的目錄項(xiàng),要后續(xù)講解。
這里4條記錄的 min_rec_flag 都是 0,表示都不是 B+ 樹非葉子節(jié)點(diǎn)中的最小的目錄項(xiàng)記錄。
3. n_owned
要下一章講解。
4. heap_no
表示當(dāng)前記錄在頁面堆中的相對位置。
上面的4條記錄是抽象的描述,實(shí)際上這些記錄都是一條一條緊密無縫排列在一起的,這就是堆(heap)。
為了方便管理,把一條記錄在堆中的相對位置稱為 heap_no。
- 在頁面前面的記錄 heap_no 相對較小
- 在頁面后面的記錄 heap_no 相對較大
- 每申請一條記錄的存儲空間時(shí),該記錄比物理位置在它之前的那條記錄的 heap_no 值大 1
上述 4 條記錄的 heap_no 分別為 2、3、4、5,嗯?怎么沒有 0 和 1?
虛擬記錄-Infimum 和 Supremum
這個在本文第二部分有提到過。其實(shí)這2條記錄是頁里自動添加的:
Infimum
:代表頁面中的最小記錄
Supremum
:代表頁面中的最大記錄
作者規(guī)定,無論向頁中插入了多少條記錄,任何用戶記錄都比 Infimum 記錄大,都比 Supremum 記錄小。
這 2 條虛擬記錄的結(jié)構(gòu)也很簡單。
所以,對于上面插入的 4 條用戶記錄,還應(yīng)該加上這2個默認(rèn)記錄,而且位置最靠前。
另外,還需要注意,當(dāng)堆中記錄的 heap_no 值分配后,就不會發(fā)生改動。即使刪除了堆中的某條記錄,這條被刪記錄的 heap_no 值也仍然不變。
5. record_type
這個屬性表示當(dāng)前記錄的類型,共 4 種:
0:表示普通記錄1:表示 B+ 樹非葉節(jié)點(diǎn)的目錄項(xiàng)記錄2:表示 Infimum 記錄3:表示 Supremum 記錄
6. next_record
這個屬性很重要,表示從當(dāng)前記錄的真實(shí)數(shù)據(jù)到下一條記錄的真實(shí)數(shù)據(jù)之間的距離。
- 屬性值為正數(shù):說明當(dāng)前記錄的下一條記錄在當(dāng)前記錄的后面。
- 屬性值為負(fù)數(shù):說明當(dāng)前記錄的下一條記錄在當(dāng)前記錄的前面。
比如,第 1 條記錄的 next_record 值為 32,那么從此記錄的真實(shí)數(shù)據(jù)地址向后找 32 字節(jié)就是下一條記錄的真實(shí)數(shù)據(jù)。再比如,當(dāng)值為 -111,那么就代表從此記錄向前找 111 字節(jié)。
很熟悉?沒錯,就是鏈表。
- 下一條記錄,是指按照主鍵從小到大排列的下一條。
- Infrimum 記錄的下一條記錄,就是本頁中主鍵值最小的用戶記錄。
- 本頁主鍵值最大的用戶記錄的下一條記錄,就是 Supremum 記錄。
所以,現(xiàn)在再來重新看下記錄之間的示意圖,可以用單向鏈表來描述了:
如果這時(shí)候,刪掉其中的某條記錄,改變的是指針。
本文參考書籍:《mysql是怎樣運(yùn)行的》
以上就是Mysql InnoDB引擎中的數(shù)據(jù)頁結(jié)構(gòu)詳解的詳細(xì)內(nèi)容,更多關(guān)于Mysql InnoDB引擎數(shù)據(jù)頁結(jié)構(gòu)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
mysql導(dǎo)出查詢結(jié)果到csv的實(shí)現(xiàn)方法
下面小編就為大家?guī)硪黄猰ysql導(dǎo)出查詢結(jié)果到csv的實(shí)現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-04-04單個select語句實(shí)現(xiàn)MySQL查詢統(tǒng)計(jì)次數(shù)
MySQL中查詢統(tǒng)計(jì)次數(shù)往往語句寫法很復(fù)雜,下文就教您一個只用單個select語句就實(shí)現(xiàn)的方法,希望對您能夠有所幫助2014-05-05簡單了解mysql InnoDB MyISAM相關(guān)區(qū)別
這篇文章主要介紹了簡單了解mysql InnoDB MyISAM相關(guān)區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09Mysql5.7在windows7下my.ini文件加載路徑及數(shù)據(jù)位置修改方法
這篇文章主要介紹了Mysql5.7在windows7下my.ini文件加載路徑及數(shù)據(jù)位置修改方法,需要的朋友可以參考下2018-02-02詳解MySQL中的數(shù)據(jù)類型和schema優(yōu)化
這篇文章主要介紹了MySQL中的數(shù)據(jù)類型和schema優(yōu)化的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)MySQL的知識,感興趣的朋友可以了解下2020-10-10mysql多表聯(lián)合查詢返回一張表的內(nèi)容實(shí)現(xiàn)代碼
在使用mysql多表聯(lián)合查詢時(shí)怎樣可以做到只返回返回一張表的內(nèi)容,本文將詳細(xì)介紹,需要了解的朋友可以參考下2012-12-12