淺談MySQL之淺入深出頁(yè)原理
一、頁(yè)的概覽
我們往 MySQL 插入的數(shù)據(jù)最終都是存在頁(yè)中的。在 InnoDB 中的設(shè)計(jì)中,頁(yè)與頁(yè)之間是通過(guò)一個(gè)雙向鏈表連接起來(lái)。
而存儲(chǔ)在頁(yè)中的一行一行的數(shù)據(jù)則是通過(guò)單鏈表連接起來(lái)的。

上圖中的 User Records 的區(qū)域就是用來(lái)存儲(chǔ)行數(shù)據(jù)的。那 InnoDB 為什么要這么設(shè)計(jì)?假設(shè)我們沒(méi)有頁(yè)這個(gè)概念,那么當(dāng)我們查詢時(shí),成千上萬(wàn)的數(shù)據(jù)要如何做到快速的查詢出結(jié)果?眾所周知,MySQL 的性能是不錯(cuò)的,而如果沒(méi)有頁(yè),我們剩下的只能是逐條逐條的遍歷數(shù)據(jù)了。
那頁(yè)是如何做到快速查詢的呢?在當(dāng)前頁(yè)中,可以通過(guò) User Records 中的連接每條記錄的單鏈表來(lái)進(jìn)行遍歷,如果在當(dāng)前頁(yè)中沒(méi)有找到,則可以通過(guò)下一頁(yè)指針快速的跳到下一頁(yè)進(jìn)行查詢。
二、Infimum 和 Supremum
有人可能會(huì)說(shuō)了,你在 User Records 中還不是通過(guò)遍歷來(lái)解決的,你就是簡(jiǎn)單的把數(shù)據(jù)分了個(gè)組而已。如果我的數(shù)據(jù)根本不在當(dāng)前這個(gè)頁(yè)中,那我難道還是得把之前的頁(yè)中的每一條數(shù)據(jù)全部遍歷完?這效率也太低了
當(dāng)然,MySQL 也考慮到了這個(gè)問(wèn)題,所以實(shí)際上在頁(yè)中還存在一塊區(qū)域叫做 The Infimum and Supremum Records ,代表了當(dāng)前頁(yè)中最大和最小的記錄。

有了 Infimum Record 和 Supremum Record ,現(xiàn)在查詢不需要將某一頁(yè)的 User Records 全部遍歷完,只需要將這兩個(gè)記錄和待查詢的目標(biāo)記錄進(jìn)行比較。比如我要查詢的數(shù)據(jù) id = 101 ,那很明顯不在當(dāng)前頁(yè)。接下來(lái)就可以通過(guò)下一頁(yè)指針跳到下頁(yè)進(jìn)行檢索。
三、使用Page Directory
可能有人又會(huì)說(shuō)了,你這 User Records 里不也全是單鏈表嗎?即使我知道我要找的數(shù)據(jù)在當(dāng)前頁(yè),那最壞的情況下,不還是得挨個(gè)挨個(gè)的遍歷100次才能找到我要找的數(shù)據(jù)?你管這也叫效率高?
不得不說(shuō),這的確是個(gè)問(wèn)題,不過(guò)是一個(gè) MySQL 已經(jīng)考慮到的問(wèn)題。不錯(cuò),挨個(gè)遍歷確實(shí)效率很低。為了解決這個(gè)問(wèn)題,MySQL 又在頁(yè)中加入了另一個(gè)區(qū)域 Page Directory 。

顧名思義,Page Directory 是個(gè)目錄,里面有很多個(gè)槽位(Slots),每一個(gè)槽位都指向了一條 User Records 中的記錄。大家可以看到,每隔幾條數(shù)據(jù),就會(huì)創(chuàng)建一個(gè)槽位。其實(shí)我圖中給出的數(shù)據(jù)是非常嚴(yán)格按照其設(shè)定來(lái)的,在一個(gè)完整的頁(yè)中,每隔6條數(shù)據(jù)就會(huì)有一個(gè) Slot。
Page Directory 的設(shè)計(jì)不知道有沒(méi)有讓你想起另一個(gè)數(shù)據(jù)結(jié)構(gòu)——跳表,只不過(guò)這里只抽象了一層索引
MySQL 會(huì)在新增數(shù)據(jù)的時(shí)候就將對(duì)應(yīng)的 Slot 創(chuàng)建好,有了 Page Directory ,就可以對(duì)一張頁(yè)的數(shù)據(jù)進(jìn)行粗略的二分查找。至于為什么是粗略,畢竟 Page Directory 中不是完整的數(shù)據(jù),二分查找出來(lái)的結(jié)果只能是個(gè)大概的位置,找到了這個(gè)大概的位置之后,還需要回到 User Records 中繼續(xù)的進(jìn)行挨個(gè)遍歷匹配。
不過(guò)這樣的效率已經(jīng)比我們剛開(kāi)始聊的原始版本高了很多了。

四、頁(yè)的真實(shí)面貌
如果我開(kāi)篇就把頁(yè)的各種組成部分,各種概念直接拋出來(lái),首先我自己接受不了,這樣顯得很僵硬。其次,對(duì)頁(yè)不熟悉的人應(yīng)該是不太能理解頁(yè)為什么要這么設(shè)計(jì)的。所以我按照查詢一條數(shù)據(jù)的一套思路,把頁(yè)的大致的面貌呈現(xiàn)給了大家。
實(shí)際上,頁(yè)上還存儲(chǔ)了很多其他的字段,也還有其他的區(qū)域,但是這些都不會(huì)影響到我們對(duì)頁(yè)的理解。所以,在對(duì)頁(yè)有了一個(gè)較為清晰的認(rèn)知之后,我們就可以來(lái)看看真實(shí)的頁(yè)到底長(zhǎng)啥樣了。

上圖就是頁(yè)的實(shí)際全部組成,除了我們之前提到過(guò)的,還多了一些之前沒(méi)有聊過(guò)的,例如 File Header、Page Header、Free Space、File Tailer 。我們一個(gè)一個(gè)來(lái)看。
4.1、File Header
其實(shí)File Header 在上文已經(jīng)聊過(guò)了,只是不叫這個(gè)名字。上面提到的上一頁(yè)指針和下一頁(yè)指針其實(shí)就是屬于File Header的,除此之外還有很多其他的數(shù)據(jù)。

其實(shí)我比較抗拒把一堆參數(shù)列出來(lái),告訴你這個(gè)大小多少,那個(gè)用來(lái)干嘛。對(duì)于我們需要詳細(xì)了解頁(yè)來(lái)說(shuō),其實(shí)暫時(shí)只需要知道兩個(gè)就足夠了,分別是:
- FIL_PAGE_PREV
- FIL_PAGE_NEXT
這兩個(gè)變量就是上文提到過(guò)的上一頁(yè)指針和下一頁(yè)指針,說(shuō)是指針,是為了方便大家理解,實(shí)際上是頁(yè)在磁盤(pán)上的偏移量。
4.2、Page Header
比起 File Header ,Page Header 中的數(shù)據(jù)對(duì)我們來(lái)說(shuō)就顯得更加熟悉了,我這里畫(huà)了一張圖,把里面的內(nèi)容詳細(xì)的列了出來(lái)。

這里全列出來(lái)是因?yàn)榱私膺@些參數(shù)的含義和為什么要設(shè)置參數(shù),能夠更好的幫助我們了解頁(yè)的原理和構(gòu)造,具體的看圖說(shuō)話就行。
這里也很想吐槽,太多博客都寫(xiě)的太僵硬,比如參數(shù) PAGE_HEAP_TOP ,這里的 HEAP 很多博客都直接叫堆。這就跟你給Init寫(xiě)注釋叫初始化一樣,還不如不寫(xiě)。實(shí)際上你去研究一下就會(huì)知道,這里的堆實(shí)際上就是指User Records。
里面有個(gè)兩個(gè)參數(shù)可能會(huì)有點(diǎn)混淆,分別是PAGE_N_HEAP和PAGE_N_RECS ,都是當(dāng)前 User Records 中記錄的數(shù)量,唯一的不同在于,PAGE_N_HEAP 中是包含了被標(biāo)記為刪除的記錄的, 而 PAGE_N_RECS 中就是實(shí)際上我們能夠查詢到的所有數(shù)據(jù)。
4.3、Infimum & Supremum Records
上文中提到,Infimum & Supremum Records會(huì)記錄當(dāng)前頁(yè)最大最小記錄。實(shí)際上不準(zhǔn)確,更準(zhǔn)確的描述是最小記錄和最大紀(jì)錄的開(kāi)區(qū)間。因?yàn)閷?shí)際上 Infimum Records 會(huì)比當(dāng)前頁(yè)中的最小值還要小,而 Supremum Records 會(huì)比當(dāng)前頁(yè)中的最大值要大。
4.4、User Records
User Records 可以說(shuō)是我們平時(shí)接觸的最多的部分了,畢竟我們的數(shù)據(jù)最終都在這。頁(yè)被初始化之后,User Records 中是沒(méi)有數(shù)據(jù)的,隨著系統(tǒng)運(yùn)行,數(shù)據(jù)產(chǎn)生,User Records 中的數(shù)據(jù)會(huì)不斷的膨脹,相應(yīng)的 Free Space 空間會(huì)慢慢的變小。
關(guān)于 User Records 中的概念,之前已經(jīng)聊過(guò)了。這里只聊我認(rèn)為很關(guān)鍵的一點(diǎn),那就是順序。
我們知道,在聚簇索引中,Key 實(shí)際上會(huì)按照 Primary Key 的順序來(lái)進(jìn)行排列。那在 User Records 中也會(huì)這樣嗎?我們插入一條新的數(shù)據(jù)到 User Records 中時(shí),是否也會(huì)按照 Primary Key 的順序來(lái)對(duì)已有的數(shù)據(jù)重排序?
答案是不會(huì),因?yàn)檫@樣會(huì)拉低 MySQL 處理的效率。
User Records 中的數(shù)據(jù)是由單鏈表指針的指向來(lái)保證的,也就是說(shuō),行數(shù)據(jù)實(shí)際在磁盤(pán)上的表現(xiàn),是按照插入順序來(lái)排隊(duì)的,先到的數(shù)據(jù)在前面,后來(lái)的數(shù)據(jù)在后面。只不過(guò)通過(guò) User Records 中的行數(shù)據(jù)之間的單鏈表形成了一個(gè)按照 Primary Key排列的順序。
用圖來(lái)表示,大概如下:

4.5、Free Space
這塊其實(shí)變相的在其他的模塊中討論了,最初 User Records 是完全空的,當(dāng)有新數(shù)據(jù)進(jìn)來(lái)時(shí),會(huì)來(lái) Free Space 中申請(qǐng)空間,當(dāng) Free Space 沒(méi)空間了,則說(shuō)明需要申請(qǐng)新的頁(yè)了,其他沒(méi)什么特別之處。
4.6、Page Directory
這跟上文討論的沒(méi)什么出入,就直接跳過(guò)了。
4.7、File Trailer
這塊主要是為了防止頁(yè)在刷入磁盤(pán)的過(guò)程中,由于極端的意外情況(網(wǎng)絡(luò)問(wèn)題、火災(zāi)、自然災(zāi)害)導(dǎo)致失敗,而造成數(shù)據(jù)不一致的情況,也就是說(shuō)形成了臟頁(yè)。
里面有只有一個(gè)組成部分:

五、總結(jié)
到此,我認(rèn)為關(guān)于頁(yè)的所有東西就聊的差不多了,了解了底層的頁(yè)原理,我個(gè)人認(rèn)為是有助于我們更加友好、理智的使用 MySQL 的,使其能發(fā)揮出自己應(yīng)該發(fā)揮的極致性能。
以上就是淺談MySQL之淺入深出頁(yè)原理的詳細(xì)內(nèi)容,更多關(guān)于MySQL 頁(yè)原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- php頁(yè)面,mysql數(shù)據(jù)庫(kù)轉(zhuǎn)utf-8亂碼,utf-8編碼問(wèn)題總結(jié)
- 在MySQL中使用LIMIT進(jìn)行分頁(yè)的方法
- 修改Innodb的數(shù)據(jù)頁(yè)大小以優(yōu)化MySQL的方法
- Spring MVC+MyBatis+MySQL實(shí)現(xiàn)分頁(yè)功能實(shí)例
- nodejs mysql 實(shí)現(xiàn)分頁(yè)的方法
- 詳解MySQL的limit用法和分頁(yè)查詢語(yǔ)句的性能分析
- php+mysql實(shí)現(xiàn)簡(jiǎn)單登錄注冊(cè)修改密碼網(wǎng)頁(yè)
- MySQL百萬(wàn)級(jí)數(shù)據(jù)分頁(yè)查詢優(yōu)化方案
- Mysql排序和分頁(yè)(order by&limit)及存在的坑
- MySQL百萬(wàn)級(jí)數(shù)據(jù)量分頁(yè)查詢方法及其優(yōu)化建議
相關(guān)文章
在EF中使用MySQL的方法及常見(jiàn)問(wèn)題
這篇文章主要介紹了在EF中使用MySQL的方法及常見(jiàn)問(wèn)題 的相關(guān)資料,需要的朋友可以參考下2016-06-06
Workbench連接不上阿里云服務(wù)器Ubuntu的Mysql解決方法(已測(cè))
這兩天為了解決workbench連接不上阿里云服務(wù)器的問(wèn)題,搞得頭大,網(wǎng)上搜到的教程都大同小異,但唯獨(dú)到我這就是行不通。不過(guò)好在最后終于解決了,記錄一下這個(gè)坑爹的過(guò)程,另外腳本之家小編特把這些問(wèn)題整理了一下,看完這一篇文章基本上就解決了2020-02-02
MySQL數(shù)據(jù)庫(kù)分區(qū)功能的使用教程
這篇文章主要介紹了MySQL數(shù)據(jù)庫(kù)分區(qū)功能的使用教程,文中特別講解了MySQL分表和分區(qū)的區(qū)別以及聯(lián)系,需要的朋友可以參考下2016-05-05
Mysql導(dǎo)入導(dǎo)出工具M(jìn)ysqldump和Source命令用法詳解
Mysql本身提供了命令行導(dǎo)出工具M(jìn)ysqldump和Mysql Source導(dǎo)入命令進(jìn)行SQL數(shù)據(jù)導(dǎo)入導(dǎo)出工作,通過(guò)Mysql命令行導(dǎo)出工具M(jìn)ysqldump命令能夠?qū)ysql數(shù)據(jù)導(dǎo)出為文本格式(txt)的SQL文件,通過(guò)Mysql Source命令能夠?qū)QL文件導(dǎo)入Mysql數(shù)據(jù)庫(kù)中,下面通過(guò)Mysql導(dǎo)入導(dǎo)出SQL實(shí)例詳解Mysqldump和Source命令的用法2012-09-09
允許遠(yuǎn)程用戶訪問(wèn)mysql服務(wù)sql語(yǔ)句
本節(jié)主要介紹了如何允許遠(yuǎn)程用戶訪問(wèn)mysql服務(wù),本例授權(quán)192.168.14.1 主機(jī)的cakephp用戶訪問(wèn)cakephp數(shù)據(jù)庫(kù)2014-07-07
Jaspersoft?Studio添加mysql數(shù)據(jù)庫(kù)配置步驟
這篇文章主要為大家介紹了Jaspersoft?Studio添加mysql數(shù)據(jù)庫(kù)配置的步驟過(guò)程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-02-02
MySQL關(guān)于字符串中數(shù)字排序的問(wèn)題分析
這篇文章主要介紹了MySQL關(guān)于字符串中數(shù)字排序的問(wèn)題,結(jié)合實(shí)例形式分析了mysql按照數(shù)值排序的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06

