欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文搞懂MySQL索引頁結(jié)構(gòu)

 更新時間:2022年02月28日 11:14:37   作者:程序員小潘  
本文主要介紹了MySQL索引頁結(jié)構(gòu),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下

1. 前言

「頁」是InnoDB管理存儲空間的基本單位,也是內(nèi)存和磁盤交互的基本單位。也就是說,哪怕你需要1字節(jié)的數(shù)據(jù),InnoDB也會讀取整個頁的數(shù)據(jù),下次讀取的數(shù)據(jù)如果恰巧也在這個頁里,就能命中緩存了。寫也是一樣的,寫數(shù)據(jù)前要先把頁加載到內(nèi)存,然后在內(nèi)存中修改,該頁被記為「臟頁」,臟頁淘汰之前必須刷盤。

InnoDB有很多類型的頁,它們的用處也各不相同。比如:有存放undo日志的頁、有存放INODE信息的頁、有存放Change Buffer信息的頁、存放用戶記錄數(shù)據(jù)的頁等等。今天我們要聊的,就是最基礎(chǔ)也是最重要的,存放用戶記錄數(shù)據(jù)的「索引頁」。

2. 索引頁結(jié)構(gòu)

InnoDB默認(rèn)的頁大小是16KB,在初始化表空間之前可以在配置文件中進(jìn)行配置,一旦初始化完成就不可再變更了。查看頁大小的命令如下,顯示的是字節(jié)數(shù)。

SHOW VARIABLES LIKE 'innodb_page_size';

索引頁結(jié)構(gòu)如下圖所示:

image.png

索引頁由七部分組成,其中Infimum和Supremum也屬于記錄,只不過是虛擬記錄,這里為了與用戶記錄區(qū)分開,還是決定將兩者拆開。

名稱大小描述
File Header38字節(jié)所有頁的通用文件頭信息
Page Header56字節(jié)索引頁特有的頁頭信息
Infimum+Supremum26字節(jié)頁中虛擬的最小、最大記錄
User Records變長用戶記錄數(shù)據(jù)
Free Space變長空閑空間
Page Directory變長頁目錄,加速頁內(nèi)數(shù)據(jù)檢索效率
File Trailer8字節(jié)所有頁的通用文件尾信息,校驗頁是否完整

2.1 File Header

File Header是所有頁都有的一個通用的結(jié)構(gòu),占用固定的38字節(jié),它記錄了頁的一些通用的狀態(tài)信息,例如:頁的頁號、Checksum、把頁串聯(lián)成雙向鏈表的指針、頁的類型等等。

名稱大小描述
FIL_PAGE_SPACE_OR_CHECKSUM4字節(jié)新版本中代表頁的校驗和Checksum
FIL_PAGE_OFFSET4字節(jié)頁號
FIL_PAGE_PREV4字節(jié)上一個頁的頁號
FIL_PAGE_NEXT4字節(jié)下一個頁的頁號
FIL_PAGE_LSN8字節(jié)頁面最后被修改時的LSN值
FIL_PAGE_TYPE2字節(jié)頁的類型
FIL_PAGE_FILE_FLUSH_LSN8字節(jié)僅在系統(tǒng)表空間的第1個頁中使用,代表文件至少被刷新到了對應(yīng)的LSN值
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID4字節(jié)頁數(shù)據(jù)哪個表空間

FIL_PAGE_SPACE_OR_CHECKSUM

基于當(dāng)前頁計算出的校驗和(Checksum),可以把它看作是哈希值,校驗和不同,則兩個頁數(shù)據(jù)肯定不同。它的作用是InnoDB在臟頁刷盤時,有可能會遇到頁刷到一半斷電的情況,頁的頭和尾部分分別記錄校驗和,只有當(dāng)頭尾的校驗和一致的時候,才代表磁盤上的頁是完整的,否則就是一個損壞的頁。

FIL_PAGE_OFFSET

頁號,頁的唯一標(biāo)識,全局遞增的數(shù)字,InnoDB通過頁號來定位唯一的一個頁。4字節(jié)存儲,意味著一個表空間最多可以有232個頁,按照一個頁16KB計算,則一個表空間最多支持64TB的數(shù)據(jù)。

FIL_PAGE_PREV & FIL_PAGE_NEXT

一個頁大小才16KB,一張表數(shù)據(jù)其實是由N多個頁構(gòu)成的,頁與頁之間在物理上可以是不連續(xù)的,但是邏輯上要連續(xù),F(xiàn)IL_PAGE_PREV和FIL_PAGE_NEXT分別指向當(dāng)前頁的上一個頁和下一個頁的頁號,通過這兩個指針將索引頁串聯(lián)成了一個雙向鏈表。記錄與記錄之間是單向的,頁與頁之間是雙向的!

FIL_PAGE_LSN

頁面最后被修改時,對應(yīng)的LSN值。LSN的全稱是Log Sequence Number,日志序列號。它是一個遞增的數(shù)字,和事務(wù)相關(guān),這里不作贅述。

FIL_PAGE_TYPE

當(dāng)前頁的類型,InnoDB為了不同的目的設(shè)計了很多不同類型的頁,索引頁的固定值是0x45BF。

FIL_PAGE_FILE_FLUSH_LSN

僅在第1個頁中使用,用來判斷數(shù)據(jù)庫是正常關(guān)閉還是異常宕機(jī)。

FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID

僅記錄當(dāng)前頁數(shù)據(jù)哪個表空間。

2.2 Page Header

Page Header是索引頁特有的結(jié)構(gòu),占用固定的56字節(jié),它記錄了索引頁中記錄相關(guān)的狀態(tài)信息。

名稱大小描述
PAGE_N_DlR_SLOTS2字節(jié)頁目錄中的槽數(shù)量
PAGE_HEAP_TOP2字節(jié)未使用的空間最小地址,User Records和Free Space分界點
PAGE_N_HEAP2字節(jié)本頁中的記錄的數(shù)量(包括虛擬記錄和刪除記錄)
PAGE_FREE2字節(jié)第一個刪除的記錄地址,后續(xù)刪除的記錄會形成鏈表。
PAGE_GARBAGE2字節(jié)已刪除記錄占用的字節(jié)數(shù)
PAGE_LAST_INSERT2字節(jié)最后插入記錄的位置
PAGE_DIRECTION2字節(jié)記錄插入的方向
PAGE_N_DIRECTION2字節(jié)同一個方向連續(xù)插入的記錄數(shù)量
PAGE_N_RECS2字節(jié)該頁中記錄的數(shù)量(不包括虛擬記錄和刪除記錄)
PAGE_MAX_TRX_ID8字節(jié)修改當(dāng)前頁的最大事務(wù)ID,僅在二級索引中使用
PAGE_LEVEL2字節(jié)當(dāng)前頁在B+樹中所處的層級
PAGE_INDEX_ID8字節(jié)索引ID,表示當(dāng)前頁屬于哪個索引
PAGE_BTR_SEG_LEAF10字節(jié)B+樹葉子段的頭部信息,僅在B+樹的Root頁定義
PAGE_BTR_SEG_TOP10字節(jié)B+樹非葉子段的頭部信息,僅在B+樹的Root頁定義

不用每個屬性都了解,我們挑幾個比較重要的看看。

PAGE_N_DlR_SLOTS

一個頁內(nèi)可能有上千條記錄,挨個遍歷的話效率太慢了。為了提高頁內(nèi)記錄的檢索效率,InnoDB將頁內(nèi)的記錄劃分為多個組,組里最大的那條記錄相較于頁的地址偏移量會記錄到「Page Directory」部分,每個組都對應(yīng)一個槽,槽的大小是固定的2字節(jié)。該屬性記錄的就是頁內(nèi)槽的數(shù)量。

PAGE_HEAP_TOP

Free Space的起始位置,它是User Records和Free Space分界點。一個全新的頁一開始是沒有User Records部分的,每插入一條記錄,都要向Free Space申請空間,F(xiàn)ree Space耗盡就代表頁滿了。

PAGE_FREE

DELETE命令刪除記錄時,InnoDB并不會真的將記錄從磁盤中刪除,而是在記錄的頭信息里打個標(biāo)記,然后將其加入到「垃圾鏈表」中。PAGE_FREE指向的就是垃圾鏈表的表頭記錄。后面刪除的記錄,也會自動加入到鏈表里。

PAGE_DIRECTION & PAGE_N_DIRECTION

PAGE_DIRECTION表示最后一條記錄插入的方向,比上一條記錄值大則記為右邊,反之則是左邊。PAGE_N_DIRECTION表示同一方向連續(xù)插入的記錄數(shù),方向變了該值就會重置。

PAGE_LEVEL

InnoDB組織數(shù)據(jù)的形式就是B+樹,樹中的節(jié)點就是索引頁,PAGE_LEVEL代表當(dāng)前頁在B+樹中所處的層級。InnoDB規(guī)定,葉子節(jié)點層級為0,然后向上遞增。

2.3 User Records

Infimum和Supremum也屬于記錄,只是為了與用戶記錄區(qū)分開才劃分成了兩部分,我們先看User Records。

用戶記錄存放在User Records部分,一個全新的頁一開始全是Free Space,是沒有User Records部分的。每插入一條記錄都需要到Free Space申請一塊空間,并將其劃分到User Records用來存放用戶記錄。當(dāng)Free Space耗盡也就代表當(dāng)前頁已經(jīng)用完了,再有新記錄需要插入,就需要申請一個新的頁了。

image.png

還記得MySQL的行格式嗎?它決定了記錄在磁盤里的存儲格式。以COMPACT為例,存儲格式如下圖:

image.png

記錄頭信息里的字段比較關(guān)鍵,以防大家忘記,我這里再貼一下:

名稱大小(Bit)說明
預(yù)留位11沒有使用
預(yù)留位21沒有使用
deleted_flag1記錄刪除標(biāo)記
min_rec_flag1B+樹非葉子節(jié)點的最小目錄項標(biāo)記
n_owned4同一頁內(nèi)同一組里最大的記錄會記錄組里的記錄數(shù)量,其余記錄該值為0
heap_no13當(dāng)前記錄在頁面堆里的相對位置
record_type3記錄類型。0:普通記錄,1:B+樹非葉子節(jié)點目錄項記錄,2:Infimum記錄,3:Supremum記錄.
next_record16下一條記錄的相對位置

記錄頭信息的最后2字節(jié)用來連接下一條記錄,將頁內(nèi)所有記錄串聯(lián)成一個單向鏈表。所以我們隱藏變長字段長度列表和NULL值列表,記錄的格式應(yīng)該是這樣的:

image.png

記錄是怎么排序的?
我們已經(jīng)知道,頁內(nèi)的記錄會自動串聯(lián)成一個單向鏈表。那這個鏈表的編排順序是什么呢?是按照記錄的插入時間排序的嗎?其實不是的,如果表有主鍵,會根據(jù)主鍵排序;沒主鍵有唯一非空索引,會根據(jù)該索引排序;兩者都沒有,InnoDB會自動生成一個row_id列并根據(jù)該列進(jìn)行排序。

若無特殊說明,本文均假定表有主鍵。

2.4 Infimum & Supremum

Infimum和Supremum是索引頁內(nèi)的兩條虛擬記錄,InnoDB規(guī)定所有索引頁都會有這兩條記錄,而且所有的用戶記錄都比Infimum大,都比Supremum小。
記錄頭信息里的heap_no代表記錄在堆里的相對位置,該值越小代表記錄越靠前。細(xì)心的同學(xué)會發(fā)現(xiàn),上圖中的用戶記錄heap_no值是從2開始的,那0和1呢?不說你也肯定猜到了,就是被Infimum和Supremum占用了。Infimum和Supremum的heap_no值分別是0和1,它倆在所有用戶記錄的最前面。

Infimum和Supremum結(jié)構(gòu)非常的簡單,和用戶記錄一樣也有頭信息,真實數(shù)據(jù)部分是固定的字符串,如下圖所示:

image.png

我們把這兩條虛擬記錄也加入到記錄里面,完整的結(jié)構(gòu)就是下面這樣的:

image.png

Supremum記錄的next_record屬性為0,代表它已經(jīng)沒有下一條記錄了。

2.5 Page Directory

Free Space沒什么好說的,就是一塊未被使用的空閑空間。

Page Directory也叫作「頁目錄」,它的目的是提高頁內(nèi)記錄的檢索效率。相較于一張表幾千萬的記錄來說,一個頁內(nèi)幾百上千條記錄已經(jīng)是很少很少了??杉幢闳绱?,它也有幾百上千條啊,如果頁內(nèi)檢索記錄只能挨個遍歷的話,那也太低效了。別忘了,頁內(nèi)的記錄是根據(jù)索引值排好序的,我們可以巧用「二分法」來快速查找。

具體做法是:將頁內(nèi)所有非刪除的記錄劃分為N個組,每個組里最后一條記錄(即主鍵最大的記錄)稱作“大哥”,其余記錄是“小弟”,“大哥”的n_owned屬性記錄了組內(nèi)的記錄數(shù)量。將“大哥”在頁內(nèi)的地址偏移量提取出來,按順序依次從File Trailer部分往前寫,每個地址偏移量占用2字節(jié),稱作一個「槽」,Page Directory就是由這些槽構(gòu)成的。
InnoDB對于分組內(nèi)的記錄數(shù)量有一些規(guī)定:

  • Infimum記錄所在分組,只能有一條記錄。
  • Supremum記錄所在分組,允許有1~8條記錄。
  • 其余分組,允許有4~8條記錄。

由此可見,一個組里最多有8條記錄,只要通過二分法快速定位到組,InnoDB也只需要遍歷這8條記錄,相較于遍歷頁內(nèi)所有記錄,效率要高的多。

image.png

2.6 File Trailer

File Trailer是所有頁都有的通用結(jié)構(gòu),占用固定的8字節(jié),它的主要作用就是為了校驗頁的完整性。磁盤的速度實在是太慢了,InnoDB不會每次寫點數(shù)據(jù)都直接刷新到磁盤上,那樣MySQL會慢死。而是將頁作為刷盤的基本單位,數(shù)據(jù)修改時,先改內(nèi)存里的頁,稍后再將整個頁的數(shù)據(jù)一次性刷新到磁盤里。但是這會帶來一個問題,一個頁16KB,刷到第10KB的時候磁盤斷電了怎么辦?重啟后InnoDB如何判斷磁盤里的頁數(shù)據(jù)是完整的?
?

InnoDB是這么處理的,刷盤前根據(jù)頁數(shù)據(jù)計算出一個Checksum,在頁頭和頁尾都寫一份。頁刷盤的時候,先刷頁頭再刷頁尾,當(dāng)頭尾兩個Checksum值一致的時候,代表磁盤里的頁是完整的,否則就表示頁頭刷了頁尾沒刷,那肯定是刷到一半出錯了。

大小說明
4字節(jié)頁的校驗和Checksum
4字節(jié)頁最后被修改時對應(yīng)的LSN的后4個字節(jié),正常情況下應(yīng)該與File Header里的FIL_PAGE_LSN的后4個字節(jié)相同。

3. 總結(jié)

頁是InnoDB存取數(shù)據(jù)的基本單位,默認(rèn)頁大小是16KB,InnoDB為了不同的目的設(shè)計了很多不同類型的頁,本文重點分析了存放用戶記錄的索引頁。頁的頭尾部分File Header和File Trailer是所有頁都有的一個通用結(jié)構(gòu),它們記錄了頁的一些通用狀態(tài)信息,和Checksum用來驗證頁的完整性。Page Header是索引頁特有的結(jié)構(gòu),它記錄了頁內(nèi)用戶記錄相關(guān)的狀態(tài)信息。User Records部分用來存放用戶記錄。另外,由于頁內(nèi)的記錄數(shù)量也不少,為了提高頁內(nèi)記錄的檢索效率,InnoDB在索引頁中加入了Page Directory,它通過將記錄分組,將組里最大的記錄的地址偏移量形成一個個槽,Page Directory就是由這些槽構(gòu)成的。檢索數(shù)據(jù)時,使用二分法快速定位到槽所在的組,就可以避免遍歷所有組的記錄了。

到此這篇關(guān)于MySQL索引頁結(jié)構(gòu)的文章就介紹到這了,更多相關(guān)MySQL索引頁結(jié)構(gòu)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MySql完整卸載的四個步驟詳解

    MySql完整卸載的四個步驟詳解

    有時候MySQL不能完全卸載,這時候必須通過一些途徑刪除掉注冊表和一些殘余的文件,然后才能重新安裝才可以成功,下面這篇文章主要給大家介紹了關(guān)于MySql完整卸載的四個步驟,需要的朋友可以參考下
    2022-06-06
  • 查詢MySQL中的樹型表兩種方法

    查詢MySQL中的樹型表兩種方法

    在 MySQL中查詢樹型表(即具有層級結(jié)構(gòu)的表)可以使用遞歸查詢或者使用嵌套集模型,下面介紹兩種方法查詢MySQL中的樹型表,感興趣的朋友一起看看吧
    2024-01-01
  • MySQL 存儲過程的優(yōu)缺點分析

    MySQL 存儲過程的優(yōu)缺點分析

    存儲過程(Stored Procedure)是一種在數(shù)據(jù)庫中存儲復(fù)雜程序,以便外部程序調(diào)用的一種數(shù)據(jù)庫對象。本文將分析存儲過程的優(yōu)缺點
    2021-05-05
  • MYSQL數(shù)據(jù)庫如何設(shè)置主從同步

    MYSQL數(shù)據(jù)庫如何設(shè)置主從同步

    大家好,本篇文章主要講的是MYSQL數(shù)據(jù)庫如何設(shè)置主從同步,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下
    2022-01-01
  • SQL SERVER遞歸查詢的實現(xiàn)

    SQL SERVER遞歸查詢的實現(xiàn)

    本文主要介紹了SQL SERVER遞歸查詢的實現(xiàn),遞歸查詢是通過CTE來實現(xiàn),具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • MYSQL鎖表問題的解決方法

    MYSQL鎖表問題的解決方法

    這篇文章主要介紹了MYSQL鎖表問題的解決方法,結(jié)合實例形式分析了MySQL鎖表問題的常見情況與相應(yīng)解決方法,需要的朋友可以參考下
    2016-03-03
  • SELECT INTO 和 INSERT INTO SELECT 兩種表復(fù)制語句簡單介紹

    SELECT INTO 和 INSERT INTO SELECT 兩種表復(fù)制語句簡單介紹

    Insert是T-sql中常用語句,Insert INTO table(field1,field2,...) values(value1,value2,...)這種形式的在應(yīng)用程序開發(fā)中必不可少
    2012-11-11
  • MySQL是如何保證數(shù)據(jù)的完整性

    MySQL是如何保證數(shù)據(jù)的完整性

    這篇文章主要介紹了MySQL是如何保證數(shù)據(jù)的完整性,幫助大家更好的理解和學(xué)習(xí)MySQL,感興趣的朋友可以了解下
    2020-08-08
  • 解決啟動MySQL服務(wù)時出現(xiàn)"mysql本地計算機(jī)上的MySQL服務(wù)啟動后停止"的問題

    解決啟動MySQL服務(wù)時出現(xiàn)"mysql本地計算機(jī)上的MySQL服務(wù)啟動后停止"的問題

    某一天我的MySQL啟動突然出現(xiàn)了異常:“mysql本地計算機(jī)上的MySQL服務(wù)啟動后停止,某些在未由其他服務(wù)或程序使用時將自動停止,”?,小編在網(wǎng)絡(luò)上面找了很多方法,MySQL啟動成功了,但是第二天開啟MySQL時還是出現(xiàn)了這個問題,現(xiàn)把兩種方法總結(jié)一下,需要的朋友可以參考下
    2023-11-11
  • MySQL窗口函數(shù)OVER()用法及說明

    MySQL窗口函數(shù)OVER()用法及說明

    這篇文章主要介紹了MySQL窗口函數(shù)OVER()用法及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08

最新評論