富陽(yáng)區(qū)建設(shè)局網(wǎng)站直通車怎么開(kāi)效果最佳
「mysql是怎樣運(yùn)行的」第五章 盛放記錄的大盒子—InnoDB數(shù)據(jù)頁(yè)結(jié)構(gòu)
文章目錄
- 「mysql是怎樣運(yùn)行的」第五章 盛放記錄的大盒子---InnoDB數(shù)據(jù)頁(yè)結(jié)構(gòu)
- @[toc]
- 一、不同類型的頁(yè)介紹
- 二、數(shù)據(jù)頁(yè)結(jié)構(gòu)的快速瀏覽
- 三、記錄在頁(yè)中的存儲(chǔ)
- 記錄頭信息的秘密
- 四、Page Directory(頁(yè)目錄)
- 五、Page Header(頁(yè)面頭部)
- 六、File Header(文件頭部)
- 七、File Trailer(文件尾部)
- 八、總結(jié)
文章目錄
- 「mysql是怎樣運(yùn)行的」第五章 盛放記錄的大盒子---InnoDB數(shù)據(jù)頁(yè)結(jié)構(gòu)
- @[toc]
- 一、不同類型的頁(yè)介紹
- 二、數(shù)據(jù)頁(yè)結(jié)構(gòu)的快速瀏覽
- 三、記錄在頁(yè)中的存儲(chǔ)
- 記錄頭信息的秘密
- 四、Page Directory(頁(yè)目錄)
- 五、Page Header(頁(yè)面頭部)
- 六、File Header(文件頭部)
- 七、File Trailer(文件尾部)
- 八、總結(jié)
一、不同類型的頁(yè)介紹
前邊我們簡(jiǎn)單提了一下頁(yè)的概念,它是InnoDB管理存儲(chǔ)空間的基本單位,一個(gè)頁(yè)的大小一般是16KB。InnoDB為了不同的目的而設(shè)計(jì)了許多種不同類型的頁(yè),比如存放表空間頭部信息的頁(yè),存放Insert Buffer信息的頁(yè),存放INODE信息的頁(yè),存放undo日志信息的頁(yè)等等等等。
我們聚焦的是那些存放我們表中記錄的那種類型的頁(yè),官方稱這種存放記錄的頁(yè)為索引(INDEX)頁(yè),鑒于我們還沒(méi)有了解過(guò)索引是個(gè)什么東西,而這些表中的記錄就是我們?nèi)粘?谥兴Q的數(shù) 據(jù),所以目前還是叫這種存放記錄的頁(yè)為數(shù)據(jù)頁(yè)吧。
二、數(shù)據(jù)頁(yè)結(jié)構(gòu)的快速瀏覽
數(shù)據(jù)頁(yè)代表的這塊16KB大小的存儲(chǔ)空間可以被劃分為多個(gè)部分,不同部分有不同的功能,各個(gè)部分如圖所示:
從圖中可以看出,一個(gè)InnoDB數(shù)據(jù)頁(yè)的存儲(chǔ)空間大致被劃分成了7個(gè)部分,有的部分占用的字節(jié)數(shù)是確定的,有的部分占用的字節(jié)數(shù)是不確定的。下邊我們用表格的方式來(lái)大致描述一下這7個(gè)部分都存儲(chǔ) 一些啥內(nèi)容(快速的瞅一眼就行了,后邊會(huì)詳細(xì)嘮叨的):
名稱 | 中文名 | 占用空間大小 | 簡(jiǎn)單描述 |
---|---|---|---|
File Header | 文件頭部 | 38 字節(jié) | 頁(yè)的一些通用信息 |
Page Header | 頁(yè)面頭部 | 56 字節(jié) | 數(shù)據(jù)頁(yè)專有的一些信息 |
Infimum + Supremum | 最小記錄和最大記錄 | 26 字節(jié) | 兩個(gè)虛擬的行記錄 |
User Records | 用戶記錄 | 不確定 | 實(shí)際存儲(chǔ)的行記錄內(nèi)容 |
Free Space | 空閑空間 | 不確定 | 頁(yè)中尚未使用的空間 |
Page Directory | 頁(yè)面目錄 | 不確定 | 頁(yè)中的某些記錄的相對(duì)位置 |
File Trailer | 文件尾部 | 8 字節(jié) | 校驗(yàn)頁(yè)是否完整 |
三、記錄在頁(yè)中的存儲(chǔ)
在頁(yè)的7個(gè)組成部分中,我們自己存儲(chǔ)的記錄會(huì)按照我們指定的行格式存儲(chǔ)到User Records部分。但是在一開(kāi)始生成頁(yè)的時(shí)候,其實(shí)并沒(méi)有User Records這個(gè)部分,每當(dāng)我們插入一條記錄,都會(huì)從Free Space部分,也就是尚未使用的存儲(chǔ)空間中申請(qǐng)一個(gè)記錄大小的空間劃分到User Records部分,當(dāng)Free Space部分的空間全部被User Records部分替代掉之后,也就意味著這個(gè)頁(yè)使用完了,如果還有新的 記錄插入的話,就需要去申請(qǐng)新的頁(yè)了,這個(gè)過(guò)程的圖示如下:
為了更好的管理在User Records中的這些記錄,InnoDB可費(fèi)了一番力氣呢,在哪費(fèi)力氣了呢?不就是把記錄按照指定的行格式一條一條擺在User Records部分么?其實(shí)這話還得從記錄行格式的記錄頭信 息中說(shuō)起。
記錄頭信息的秘密
從圖中可以看到,我們特意把記錄頭信息的5個(gè)字節(jié)的數(shù)據(jù)給標(biāo)出來(lái)了,說(shuō)明它很重要,我們?cè)俅蜗劝堰@些記錄頭信息中各個(gè)屬性的大體意思瀏覽一下(我們目前使用Compact行格式進(jìn)行演示):
名稱 | 大小(單位:bit) | 描述 |
---|---|---|
預(yù)留位1 | 1 | 沒(méi)有使用 |
預(yù)留位2 | 1 | 沒(méi)有使用 |
delete_mask | 1 | 標(biāo)記該記錄是否被刪除 |
min_rec_mask | 1 | B+樹(shù)的每層非葉子節(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+樹(shù)非葉節(jié)點(diǎn)記錄,2 表示最小記錄,3 表示最大記錄 |
next_record | 16 | 表示下一條記錄的相對(duì)位置 |
四、Page Directory(頁(yè)目錄)
現(xiàn)在我們了解了記錄在頁(yè)中按照主鍵值由小到大順序串聯(lián)成一個(gè)單鏈表,那如果我們想根據(jù)主鍵值查找頁(yè)中的某條記錄該咋辦呢?比如說(shuō)這樣的查詢語(yǔ)句:
SELECT * FROM page_demo WHERE c1 = 3;
最笨的辦法:從Infimum記錄(最小記錄)開(kāi)始,沿著鏈表一直往后找,總有一天會(huì)找到(或者找不到[攤手]),在找的時(shí)候還能投機(jī)取巧,因?yàn)殒湵碇懈鱾€(gè)記錄的值是按照從小到大順序排列的,所以當(dāng)鏈表的某個(gè)節(jié)點(diǎn)代表的記錄的主鍵值大于你想要查找的主鍵值時(shí),你就可以停止查找了,因?yàn)樵摴?jié)點(diǎn)后邊的節(jié)點(diǎn)的主鍵值依次遞增。
這個(gè)方法在頁(yè)中存儲(chǔ)的記錄數(shù)量比較少的情況用起來(lái)也沒(méi)啥問(wèn)題,比方說(shuō)現(xiàn)在我們的表里只有4條自己插入的記錄,所以最多找4次就可以把所有記錄都遍歷一遍,但是如果一個(gè)頁(yè)中存儲(chǔ)了非常多的記 錄,這么查找對(duì)性能來(lái)說(shuō)還是有損耗的,所以我們說(shuō)這種遍歷查找這是一個(gè)笨辦法。但是設(shè)計(jì)InnoDB的大叔們是什么人,他們能用這么笨的辦法么,當(dāng)然是要設(shè)計(jì)一種更6的查找方式嘍,他們從書(shū)的目錄中找到了靈感。
我們平常想從一本書(shū)中查找某個(gè)內(nèi)容的時(shí)候,一般會(huì)先看目錄,找到需要查找的內(nèi)容對(duì)應(yīng)的書(shū)的頁(yè)碼,然后到對(duì)應(yīng)的頁(yè)碼查看內(nèi)容。設(shè)計(jì)InnoDB的大叔們?yōu)槲覀兊挠涗浺仓谱髁艘粋€(gè)類似的目錄,他們的制作過(guò)程是這樣的:
- 將所有正常的記錄(包括最大和最小記錄,不包括標(biāo)記為已刪除的記錄)劃分為幾個(gè)組。
- 每個(gè)組的最后一條記錄(也就是組內(nèi)最大的那條記錄)的頭信息中的n_owned屬性表示該記錄擁有多少條記錄,也就是該組內(nèi)共有幾條記錄。
- 將每個(gè)組的最后一條記錄的地址偏移量單獨(dú)提取出來(lái)按順序存儲(chǔ)到靠近頁(yè)的尾部的地方,這個(gè)地方就是所謂的Page Directory,也就是頁(yè)目錄(此時(shí)應(yīng)該返回頭看看頁(yè)面各個(gè)部分的圖)。頁(yè)面目錄 中的這些地址偏移量被稱為槽(英文名:Slot),所以這個(gè)頁(yè)面目錄就是由槽組成的。
比方說(shuō)現(xiàn)在的page_demo表中正常的記錄共有6條,InnoDB會(huì)把它們分成兩組,第一組中只有一個(gè)最小記錄,第二組中是剩余的5條記錄,看下邊的示意圖:
- 現(xiàn)在
頁(yè)目錄
部分中有兩個(gè)槽,也就意味著我們的記錄被分成了兩個(gè)組,槽1
中的值是112
,代表最大記錄的地址偏移量(就是從頁(yè)面的0字節(jié)開(kāi)始數(shù),數(shù)112個(gè)字節(jié));槽0
中的值是99
,代表最小記錄的地址偏移量。 - 注意最小和最大記錄的頭信息中的
n_owned
屬性- 最小記錄的
n_owned
值為1
,這就代表著以最小記錄結(jié)尾的這個(gè)分組中只有1
條記錄,也就是最小記錄本身。 - 最大記錄的
n_owned
值為5
,這就代表著以最大記錄結(jié)尾的這個(gè)分組中只有5
條記錄,包括最大記錄本身還有我們自己插入的4
條記錄。
- 最小記錄的
99
和112
這樣的地址偏移量很不直觀,我們用箭頭指向的方式替代數(shù)字,這樣更易于我們理解,所以修改后的示意圖就是這樣:
是的,設(shè)計(jì)InnoDB
的大叔們對(duì)每個(gè)分組中的記錄條數(shù)是有規(guī)定的:對(duì)于最小記錄所在的分組只能有 *1* 條記錄,最大記錄所在的分組擁有的記錄條數(shù)只能在 *1~8* 條之間,剩下的分組中記錄的條數(shù)范圍只能在是 *4~8* 條之間。所以分組是按照下邊的步驟進(jìn)行的:
- 初始情況下一個(gè)數(shù)據(jù)頁(yè)里只有最小記錄和最大記錄兩條記錄,它們分屬于兩個(gè)分組。
- 之后每插入一條記錄,都會(huì)從
頁(yè)目錄
中找到主鍵值比本記錄的主鍵值大并且差值最小的槽,然后把該槽對(duì)應(yīng)的記錄的n_owned
值加1,表示本組內(nèi)又添加了一條記錄,直到該組中的記錄數(shù)等于8個(gè)。 - 在一個(gè)組中的記錄數(shù)等于8個(gè)后再插入一條記錄時(shí),會(huì)將組中的記錄拆分成兩個(gè)組,一個(gè)組中4條記錄,另一個(gè)5條記錄。這個(gè)過(guò)程會(huì)在
頁(yè)目錄
中新增一個(gè)槽
來(lái)記錄這個(gè)新增分組中最大的那條記錄的偏移量。
數(shù)據(jù)頁(yè)中查找指定主鍵值的記錄的過(guò)程
一個(gè)數(shù)據(jù)頁(yè)中查找指定主鍵值的記錄的過(guò)程分為兩步:
- 通過(guò)二分法確定該記錄所在的槽,并找到該槽中主鍵值最小的那條記錄。
- 通過(guò)記錄的
next_record
屬性遍歷該槽所在的組中的各個(gè)記錄。
五、Page Header(頁(yè)面頭部)
設(shè)計(jì)InnoDB的大叔們?yōu)榱四艿玫揭粋€(gè)數(shù)據(jù)頁(yè)中存儲(chǔ)的記錄的狀態(tài)信息,比如本頁(yè)中已經(jīng)存儲(chǔ)了多少條記錄,第一條記錄的地址是什么,頁(yè)目錄中存儲(chǔ)了多少個(gè)槽等等,特意在頁(yè)中定義了一個(gè)叫Page Header的部分,它是頁(yè)結(jié)構(gòu)的第二部分,這個(gè)部分占用固定的56個(gè)字節(jié),專門存儲(chǔ)各種狀態(tài)信息,具體各個(gè)字節(jié)都是干嘛的看下表:
名稱 | 占用空間大小 | 描述 |
---|---|---|
PAGE_N_DIR_SLOTS | 2 字節(jié) | 在頁(yè)目錄中的槽數(shù)量 |
PAGE_HEAP_TOP | 2 字節(jié) | 還未使用的空間最小地址,也就是說(shuō)從該地址之后就是Free Space |
PAGE_N_HEAP | 2 字節(jié) | 本頁(yè)中的記錄的數(shù)量(包括最小和最大記錄以及標(biāo)記為刪除的記錄) |
PAGE_FREE | 2 字節(jié) | 第一個(gè)已經(jīng)標(biāo)記為刪除的記錄地址(各個(gè)已刪除的記錄通過(guò)next_record 也會(huì)組成一個(gè)單鏈表,這個(gè)單鏈表中的記錄可以被重新利用) |
PAGE_GARBAGE | 2 字節(jié) | 已刪除記錄占用的字節(jié)數(shù) |
PAGE_LAST_INSERT | 2 字節(jié) | 最后插入記錄的位置 |
PAGE_DIRECTION | 2 字節(jié) | 記錄插入的方向 |
PAGE_N_DIRECTION | 2 字節(jié) | 一個(gè)方向連續(xù)插入的記錄數(shù)量 |
PAGE_N_RECS | 2 字節(jié) | 該頁(yè)中記錄的數(shù)量(不包括最小和最大記錄以及被標(biāo)記為刪除的記錄) |
PAGE_MAX_TRX_ID | 8 字節(jié) | 修改當(dāng)前頁(yè)的最大事務(wù)ID,該值僅在二級(jí)索引中定義 |
PAGE_LEVEL | 2 字節(jié) | 當(dāng)前頁(yè)在B+樹(shù)中所處的層級(jí) |
PAGE_INDEX_ID | 8 字節(jié) | 索引ID,表示當(dāng)前頁(yè)屬于哪個(gè)索引 |
PAGE_BTR_SEG_LEAF | 10 字節(jié) | B+樹(shù)葉子段的頭部信息,僅在B+樹(shù)的Root頁(yè)定義 |
PAGE_BTR_SEG_TOP | 10 字節(jié) | B+樹(shù)非葉子段的頭部信息,僅在B+樹(shù)的Root頁(yè)定義 |
在這里我們先嘮叨一下PAGE_DIRECTION
和PAGE_N_DIRECTION
的意思:
-
PAGE_DIRECTION
假如新插入的一條記錄的主鍵值比上一條記錄的主鍵值大,我們說(shuō)這條記錄的插入方向是右邊,反之則是左邊。用來(lái)表示最后一條記錄插入方向的狀態(tài)就是
PAGE_DIRECTION
。 -
PAGE_N_DIRECTION
假設(shè)連續(xù)幾次插入新記錄的方向都是一致的,
InnoDB
會(huì)把沿著同一個(gè)方向插入記錄的條數(shù)記下來(lái),這個(gè)條數(shù)就用PAGE_N_DIRECTION
這個(gè)狀態(tài)表示。當(dāng)然,如果最后一條記錄的插入方向改變了的話,這個(gè)狀態(tài)的值會(huì)被清零重新統(tǒng)計(jì)。
六、File Header(文件頭部)
上邊嘮叨的Page Header是專門針對(duì)數(shù)據(jù)頁(yè)記錄的各種狀態(tài)信息,比方說(shuō)頁(yè)里頭有多少個(gè)記錄了呀,有多少個(gè)槽了呀。我們現(xiàn)在描述的File Header針對(duì)各種類型的頁(yè)都通用,也就是說(shuō)不同類型的頁(yè)都會(huì) 以File Header作為第一個(gè)組成部分,它描述了一些針對(duì)各種頁(yè)都通用的一些信息,比方說(shuō)這個(gè)頁(yè)的編號(hào)是多少,它的上一個(gè)頁(yè)、下一個(gè)頁(yè)是誰(shuí)啦吧啦吧啦~ 這個(gè)部分占用固定的38個(gè)字節(jié),是由下邊這 些內(nèi)容組成的:
名稱 | 占用空間大小 | 描述 |
---|---|---|
FIL_PAGE_SPACE_OR_CHKSUM | 4 字節(jié) | 頁(yè)的校驗(yàn)和(checksum值) |
FIL_PAGE_OFFSET | 4 字節(jié) | 頁(yè)號(hào) |
FIL_PAGE_PREV | 4 字節(jié) | 上一個(gè)頁(yè)的頁(yè)號(hào) |
FIL_PAGE_NEXT | 4 字節(jié) | 下一個(gè)頁(yè)的頁(yè)號(hào) |
FIL_PAGE_LSN | 8 字節(jié) | 頁(yè)面被最后修改時(shí)對(duì)應(yīng)的日志序列位置(英文名是:Log Sequence Number) |
FIL_PAGE_TYPE | 2 字節(jié) | 該頁(yè)的類型 |
FIL_PAGE_FILE_FLUSH_LSN | 8 字節(jié) | 僅在系統(tǒng)表空間的一個(gè)頁(yè)中定義,代表文件至少被刷新到了對(duì)應(yīng)的LSN值 |
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID | 4 字節(jié) | 頁(yè)屬于哪個(gè)表空間 |
對(duì)照著這個(gè)表格,我們看幾個(gè)目前比較重要的部分:
-
FIL_PAGE_SPACE_OR_CHKSUM
這個(gè)代表當(dāng)前頁(yè)面的校驗(yàn)和(checksum)。啥是個(gè)校驗(yàn)和?就是對(duì)于一個(gè)很長(zhǎng)很長(zhǎng)的字節(jié)串來(lái)說(shuō),我們會(huì)通過(guò)某種算法來(lái)計(jì)算一個(gè)比較短的值來(lái)代表這個(gè)很長(zhǎng)的字節(jié)串,這個(gè)比較短的值就稱為
校驗(yàn)和
。這樣在比較兩個(gè)很長(zhǎng)的字節(jié)串之前先比較這兩個(gè)長(zhǎng)字節(jié)串的校驗(yàn)和,如果校驗(yàn)和都不一樣兩個(gè)長(zhǎng)字節(jié)串肯定是不同的,所以省去了直接比較兩個(gè)比較長(zhǎng)的字節(jié)串的時(shí)間損耗。 -
FIL_PAGE_OFFSET
每一個(gè)
頁(yè)
都有一個(gè)單獨(dú)的頁(yè)號(hào),就跟你的身份證號(hào)碼一樣,InnoDB
通過(guò)頁(yè)號(hào)來(lái)可以唯一定位一個(gè)頁(yè)
。 -
FIL_PAGE_TYPE
這個(gè)代表當(dāng)前
頁(yè)
的類型,我們前邊說(shuō)過(guò),InnoDB
為了不同的目的而把頁(yè)分為不同的類型,我們上邊介紹的其實(shí)都是存儲(chǔ)記錄的數(shù)據(jù)頁(yè)
,其實(shí)還有很多別的類型的頁(yè),具體如下表:類型名稱 十六進(jìn)制 描述 FIL_PAGE_TYPE_ALLOCATED
0x0000 最新分配,還沒(méi)使用 FIL_PAGE_UNDO_LOG
0x0002 Undo日志頁(yè) FIL_PAGE_INODE
0x0003 段信息節(jié)點(diǎn) FIL_PAGE_IBUF_FREE_LIST
0x0004 Insert Buffer空閑列表 FIL_PAGE_IBUF_BITMAP
0x0005 Insert Buffer位圖 FIL_PAGE_TYPE_SYS
0x0006 系統(tǒng)頁(yè) FIL_PAGE_TYPE_TRX_SYS
0x0007 事務(wù)系統(tǒng)數(shù)據(jù) FIL_PAGE_TYPE_FSP_HDR
0x0008 表空間頭部信息 FIL_PAGE_TYPE_XDES
0x0009 擴(kuò)展描述頁(yè) FIL_PAGE_TYPE_BLOB
0x000A BLOB頁(yè) FIL_PAGE_INDEX
0x45BF 索引頁(yè),也就是我們所說(shuō)的 數(shù)據(jù)頁(yè)
我們存放記錄的數(shù)據(jù)頁(yè)的類型其實(shí)是
FIL_PAGE_INDEX
,也就是所謂的索引頁(yè)
。 -
FIL_PAGE_PREV
和FIL_PAGE_NEXT
我們前邊強(qiáng)調(diào)過(guò),
InnoDB
都是以頁(yè)為單位存放數(shù)據(jù)的,有時(shí)候我們存放某種類型的數(shù)據(jù)占用的空間非常大(比方說(shuō)一張表中可以有成千上萬(wàn)條記錄),InnoDB
可能不可以一次性為這么多數(shù)據(jù)分配一個(gè)非常大的存儲(chǔ)空間,如果分散到多個(gè)不連續(xù)的頁(yè)中存儲(chǔ)的話需要把這些頁(yè)關(guān)聯(lián)起來(lái),FIL_PAGE_PREV
和FIL_PAGE_NEXT
就分別代表本頁(yè)的上一個(gè)和下一個(gè)頁(yè)的頁(yè)號(hào)。這樣通過(guò)建立一個(gè)雙向鏈表把許許多多的頁(yè)就都串聯(lián)起來(lái)了,而無(wú)需這些頁(yè)在物理上真正連著。需要注意的是,并不是所有類型的頁(yè)都有上一個(gè)和下一個(gè)頁(yè)的屬性,不過(guò)我們本集中嘮叨的數(shù)據(jù)頁(yè)
(也就是類型為FIL_PAGE_INDEX
的頁(yè))是有這兩個(gè)屬性的,所以所有的數(shù)據(jù)頁(yè)其實(shí)是一個(gè)雙鏈表,就像這樣:
七、File Trailer(文件尾部)
我們知道InnoDB
存儲(chǔ)引擎會(huì)把數(shù)據(jù)存儲(chǔ)到磁盤上,但是磁盤速度太慢,需要以頁(yè)
為單位把數(shù)據(jù)加載到內(nèi)存中處理,如果該頁(yè)中的數(shù)據(jù)在內(nèi)存中被修改了,那么在修改后的某個(gè)時(shí)間需要把數(shù)據(jù)同步到磁盤中。但是在同步了一半的時(shí)候中斷電了咋辦,這不是莫名尷尬么?為了檢測(cè)一個(gè)頁(yè)是否完整(也就是在同步的時(shí)候有沒(méi)有發(fā)生只同步一半的尷尬情況),設(shè)計(jì)InnoDB
的大叔們?cè)诿總€(gè)頁(yè)的尾部都加了一個(gè)File Trailer
部分,這個(gè)部分由8
個(gè)字節(jié)組成,可以分成2個(gè)小部分:
-
前4個(gè)字節(jié)代表頁(yè)的校驗(yàn)和
這個(gè)部分是和
File Header
中的校驗(yàn)和相對(duì)應(yīng)的。每當(dāng)一個(gè)頁(yè)面在內(nèi)存中修改了,在同步之前就要把它的校驗(yàn)和算出來(lái),因?yàn)?code>File Header在頁(yè)面的前邊,所以校驗(yàn)和會(huì)被首先同步到磁盤,當(dāng)完全寫(xiě)完時(shí),校驗(yàn)和也會(huì)被寫(xiě)到頁(yè)的尾部,如果完全同步成功,則頁(yè)的首部和尾部的校驗(yàn)和應(yīng)該是一致的。如果寫(xiě)了一半兒斷電了,那么在File Header
中的校驗(yàn)和就代表著已經(jīng)修改過(guò)的頁(yè),而在File Trialer
中的校驗(yàn)和代表著原先的頁(yè),二者不同則意味著同步中間出了錯(cuò)。 -
后4個(gè)字節(jié)代表頁(yè)面被最后修改時(shí)對(duì)應(yīng)的日志序列位置(LSN)
這個(gè)部分也是為了校驗(yàn)頁(yè)的完整性的,只不過(guò)我們目前還沒(méi)說(shuō)
LSN
是個(gè)什么意思,所以大家可以先不用管這個(gè)屬性。
這個(gè)File Trailer
與File Header
類似,都是所有類型的頁(yè)通用的。
八、總結(jié)
- InnoDB為了不同的目的而設(shè)計(jì)了不同類型的頁(yè),我們把用于存放記錄的頁(yè)叫做
數(shù)據(jù)頁(yè)
。 - 一個(gè)數(shù)據(jù)頁(yè)可以被大致劃分為7個(gè)部分,分別是
File Header
,表示頁(yè)的一些通用信息,占固定的38字節(jié)。Page Header
,表示數(shù)據(jù)頁(yè)專有的一些信息,占固定的56個(gè)字節(jié)。Infimum + Supremum
,兩個(gè)虛擬的偽記錄,分別表示頁(yè)中的最小和最大記錄,占固定的26
個(gè)字節(jié)。User Records
:真實(shí)存儲(chǔ)我們插入的記錄的部分,大小不固定。Free Space
:頁(yè)中尚未使用的部分,大小不確定。Page Directory
:頁(yè)中的某些記錄相對(duì)位置,也就是各個(gè)槽在頁(yè)面中的地址偏移量,大小不固定,插入的記錄越多,這個(gè)部分占用的空間越多。File Trailer
:用于檢驗(yàn)頁(yè)是否完整的部分,占用固定的8個(gè)字節(jié)。
- 每個(gè)記錄的頭信息中都有一個(gè)
next_record
屬性,從而使頁(yè)中的所有記錄串聯(lián)成一個(gè)單鏈表
。 InnoDB
會(huì)為把頁(yè)中的記錄劃分為若干個(gè)組,每個(gè)組的最后一個(gè)記錄的地址偏移量作為一個(gè)槽
,存放在Page Directory
中,所以在一個(gè)頁(yè)中根據(jù)主鍵查找記錄是非??斓?#xff0c;分為兩步:- 通過(guò)二分法確定該記錄所在的槽。
- 通過(guò)記錄的next_record屬性遍歷該槽所在的組中的各個(gè)記錄。
- 每個(gè)數(shù)據(jù)頁(yè)的
File Header
部分都有上一個(gè)和下一個(gè)頁(yè)的編號(hào),所以所有的數(shù)據(jù)頁(yè)會(huì)組成一個(gè)雙鏈表
。 - 為保證從內(nèi)存中同步到磁盤的頁(yè)的完整性,在頁(yè)的首部和尾部都會(huì)存儲(chǔ)頁(yè)中數(shù)據(jù)的校驗(yàn)和和頁(yè)面最后修改時(shí)對(duì)應(yīng)的
LSN
值,如果首部和尾部的校驗(yàn)和和LSN
值校驗(yàn)不成功的話,就說(shuō)明同步過(guò)程出現(xiàn)了問(wèn)題。
參考
mysql是怎樣運(yùn)行的