談談InnoDB核心組件--Buffer Pool

前言

我們了解到buffer pool 是InnoDB獨有的一個內(nèi)存結構,之前初步了解到數(shù)據(jù)的增刪改都是在這塊內(nèi)存里面執(zhí)行。因為我們不可能直接在磁盤中對數(shù)據(jù)進行增刪改,如果對磁盤的隨機讀寫,速度會非常的慢,就更別談每秒處理上千的請求了。之前也描述過雖然是在buffer pool是在內(nèi)存中,但MySQL有非常嚴謹?shù)亩档追椒?,就算宕機了,也可以配合 redo log 來進行數(shù)據(jù)的更新和刷盤。那buffer pool究竟長什么樣子?

1. buffer pool 占用多少內(nèi)存,多少合適?

buffer pool是一個內(nèi)存數(shù)據(jù)結構,在MySQL內(nèi)部肯定是占有一定的內(nèi)存。

buffer pool的默認大小是128M,這個可以根據(jù)實際環(huán)境來進行分配。MySQL官網(wǎng)給出的建議是大小占用實際環(huán)境的80%,比如MySQL服務器是一個8核16G的機器,則可以分配12G內(nèi)存。

innodb_buffer_pool_size = 12,884,901,888
圖 1

2. buffer pool 存儲的是什么樣的數(shù)據(jù)?

大家都清楚數(shù)據(jù)庫的核心數(shù)據(jù)模型是表+字段+行的概念,也就是說數(shù)據(jù)庫有很多個表,表里面有很多的字段,每個字段都有對應的值,一行一行的數(shù)據(jù)構成了表。那有沒有可能buffer pool儲存的是一行一行的數(shù)據(jù)?

答案不是的。實際上MySQL對數(shù)據(jù)抽象出了一個數(shù)據(jù)頁的概念,他把很多行數(shù)據(jù)放在了一個數(shù)據(jù)頁里,也即是說磁盤文件里有很多的數(shù)據(jù)頁,每一個數(shù)據(jù)頁里放了很多行數(shù)據(jù)。

圖 2

所以實際上,更新一行數(shù)據(jù),數(shù)據(jù)庫找到這行數(shù)據(jù)所在的數(shù)據(jù)頁,然后從磁盤中把數(shù)據(jù)頁加載到buffer pool中。也就是說,buffer pool存放的是一個一個的數(shù)據(jù)頁。

圖 2-2

3. 磁盤中的數(shù)據(jù)頁與buffer pool中的緩存頁如何對應起來?

默認情況下,一個數(shù)據(jù)頁的大小為16kb,,包含了16kb的內(nèi)容,而buffer pool中緩存頁跟數(shù)據(jù)頁是一一對應的,即是說buffer pool中緩存頁的大小也為16kb。

圖 3

而每個緩存頁在buffer pool中都有對應的描述信息。描述信息也是一個數(shù)據(jù)塊,相當于緩存頁大小的5%,里面描述了該數(shù)據(jù)頁所屬的表空間、數(shù)據(jù)頁的編號、這個緩存頁在buffer pool中的地址等信息。每個描述信息放在buffer pool最前面,各個緩存頁放在后面。

圖 3-2

4. 初始化buffer pool

數(shù)據(jù)庫在啟動階段是如何初始化buffer pool的呢?

當數(shù)據(jù)庫啟動的時候便會向操作系統(tǒng)申請一塊內(nèi)存給buffer pool,內(nèi)存區(qū)域申請完畢后,就會按照默認的緩存頁大小16kb和描述信息塊的800左右的字節(jié)在buffer pool中劃分出來一個一個的緩存頁和他們所對應的描述數(shù)據(jù)塊。

只不過這時的緩存頁都是空的,等數(shù)據(jù)庫完全運行起來,我們執(zhí)行增刪查改的時候,才會把對應的數(shù)據(jù)頁加載到緩存頁中。

5. 如何區(qū)分空閑緩存頁--free鏈表

在不停的增刪改查下,MySQL會不停的把數(shù)據(jù)頁從磁盤中加載到緩存中里。隨著16kb的數(shù)據(jù)頁不停的加載,那是如何判斷哪些緩存頁是空閑的?

MySQL為buffer pool設計了free鏈表。在buffer pool中,每一個空閑的緩存頁對應的描述數(shù)據(jù)塊通過頭節(jié)點和尾節(jié)點連接在一起,形成一個free鏈表。

每當數(shù)據(jù)頁加載到buffer pool中,對應描述數(shù)據(jù)塊就會記錄相關的信息,找到相應的緩存頁緩存數(shù)據(jù)頁,然后從free鏈表中移除此描述數(shù)據(jù)塊。

即如果緩存頁是空閑的,他的描述數(shù)據(jù)塊必定存在這個雙向鏈表中。

圖 5

除此之外,還會有一個基礎節(jié)點,引用鏈表的頭節(jié)點和尾節(jié)點,記錄free鏈表的節(jié)點數(shù),即空閑緩存頁的個數(shù)。

free鏈表是由buffer pool的空閑描述數(shù)據(jù)塊組成,并不會占用額外的內(nèi)存空間,如果緩存頁加載了數(shù)據(jù),只會把節(jié)點從鏈表中移除。

6. 把磁盤的數(shù)據(jù)頁加載到buffer pool的緩存頁

有了free鏈表,這一步就很簡單了。

首先從free鏈表中獲取一個描述數(shù)據(jù)塊,就可以獲取到對應的空閑緩存頁,寫入描述信息,把磁盤上的數(shù)據(jù)頁讀取到緩存頁,最后移除對應的描述數(shù)據(jù)塊。

圖 6

關于數(shù)據(jù)塊如何從free鏈表中移除,可以參考以下偽代碼:
假設節(jié)點02,上一個節(jié)點是01,下一個節(jié)點是03

DescriprionDataBlock() {
    block_id = 02;
    free_pre = 01;
    free_next = 03;
}

當03節(jié)點從free鏈表中移除,則把free_next 設置為null就可以了,03在free鏈表中就是去了引用關系。

DescriprionDataBlock() {
    block_id = 02;
    free_pre = 01;
    free_next = null;
}

7. 如何判斷數(shù)據(jù)頁有沒有被緩存?

在執(zhí)行增刪查改的時候,首先肯定是判斷數(shù)據(jù)頁有沒有被緩存,如果沒有,則走上面的邏輯,從free鏈表中找一個空閑的緩存頁,從磁盤讀取數(shù)據(jù)頁寫入緩存頁,寫入描述信息,從free鏈表中移除此描述數(shù)據(jù)塊。

如果數(shù)據(jù)頁已經(jīng)存在緩存里,那么就會直接使用。

所以MySQL還會有一個哈希表數(shù)據(jù)結構,用表空間號+數(shù)據(jù)頁編號作為kye,緩存頁地址作為value。

當需要數(shù)據(jù)頁的時,就會用表空間號+數(shù)據(jù)頁編號作為key去哈希表查,如果沒有就讀取數(shù)據(jù)頁,如果有就說明數(shù)據(jù)頁已經(jīng)被緩存了。

也就是說,每將數(shù)據(jù)頁讀取到緩存頁,都會在哈希表寫入一個key-value。

圖 7

8. 臟頁--flush鏈表

在buffer pool的緩存頁中,有查詢出來的還沒有更新的,有已經(jīng)更新過數(shù)據(jù)的。更新過數(shù)據(jù)的緩存頁肯定和磁盤上的不一致,這些緩存頁便是臟數(shù)據(jù),臟頁。

這些臟頁最終都是被隨機刷入磁盤,但如何區(qū)分哪些緩存頁是臟頁?

數(shù)據(jù)庫引入了跟free鏈表相似的flush鏈表,本質(zhì)上flush鏈表也是有緩存頁的描述數(shù)據(jù)塊的兩個指針組成的一個鏈表,只是這里是被修改過的緩存頁的描述數(shù)據(jù)塊。

但凡修改過的緩存頁,都會把它相應的描述數(shù)據(jù)塊加入到flush鏈表中,等待刷盤。

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

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

  • 夜鶯2517閱讀 128,214評論 1 9
  • 版本:ios 1.2.1 亮點: 1.app角標可以實時更新天氣溫度或選擇空氣質(zhì)量,建議處女座就不要選了,不然老想...
    我就是沉沉閱讀 7,510評論 1 6
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月,有人笑有人哭,有人歡樂有人憂愁,有人驚喜有人失落,有的覺得收獲滿滿有...
    陌忘宇閱讀 8,896評論 28 54
  • 兔子雖然是枚小碩 但學校的碩士四人寢不夠 就被分到了博士樓里 兩人一間 在學校的最西邊 靠山 兔子的室友身體不好 ...
    待業(yè)的兔子閱讀 2,786評論 2 9

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