從磁盤讀取數據頁到Buffer Pool時,free鏈表有什麼用?

1、數據庫啓動的時候,是如何初始化Buffer Pool的?

現在我們已經搞明白一件事兒了,那就是數據庫的Buffer Pool到底長成個什麼樣,大家想必都是理解了

其實說白了,裏面就是會包含很多個緩存頁,同時每個緩存頁還有一個描述數據,也可以叫做是控制數據,但是我個人是比較傾向於叫做描述數據,或者緩存頁的元數據,都是可以的。

那麼在數據庫啓動的時候,他是如何初始化Buffer Pool的呢?

其實這個也很簡單,數據庫只要一啓動,就會按照你設置的Buffer Pool大小,稍微再加大一點,去找操作系統申請一塊內存區域,作爲Buffer Pool的內存區域。

然後當內存區域申請完畢之後,數據庫就會按照默認的緩存頁的16KB的大小以及對應的800個字節左右的描述數據的大小,在Buffer Pool中劃分出來一個一個的緩存頁和一個一個的他們對應的描述數據。

然後當數據庫把Buffer Pool劃分完畢之後,看起來就是之前我們看到的那張圖了,如下圖所示。

在這裏插入圖片描述
只不過這個時候,Buffer Pool中的一個一個的緩存頁都是空的,裏面什麼都沒有,要等數據庫運行起來之後,當我們要對數據執行增刪改查的操作的時候,纔會把數據對應的頁從磁盤文件裏讀取出來,放入Buffer Pool中的緩存頁中。

2、我們怎麼知道哪些緩存頁是空閒的呢?

接着我們來看下一個問題,當你的數據庫運行起來之後,你肯定會不停的執行增刪改查的操作,此時就需要不停的從磁盤上讀取一個一個的數據頁放入Buffer Pool中的對應的緩存頁裏去,把數據緩存起來,那麼以後就可以對這個數據在內存裏執行增刪改查了。

但是此時在從磁盤上讀取數據頁放入Buffer Pool中的緩存頁的時候,必然涉及到一個問題,那就是哪些緩存頁是空閒的?

因爲默認情況下磁盤上的數據頁和緩存頁是一 一對應起來的,都是16KB,一個數據頁對應一個緩存頁。

所以我們必須要知道Buffer Pool中哪些緩存頁是空閒的狀態。

所以數據庫會爲Buffer Pool設計一個free鏈表,他是一個雙向鏈表數據結構,這個free鏈表裏,每個節點就是一個空閒的緩存頁的描述數據塊的地址,也就是說,只要你一個緩存頁是空閒的,那麼他的描述數據塊就會被放入這個free鏈表中。

剛開始數據庫啓動的時候,可能所有的緩存頁都是空閒的,因爲此時可能是一個空的數據庫,一條數據都沒有,所以此時所有緩存頁的描述數據塊,都會被放入這個free鏈表中

我們看下圖所示
在這裏插入圖片描述
大家可以看到上面出現了一個free鏈表,這個free鏈表裏面就是各個緩存頁的描述數據塊,只要緩存頁是空閒的,那麼他們對應的描述數據塊就會加入到這個free鏈表中,每個節點都會雙向鏈接自己的前後節點,組成一個雙向鏈表。

除此之外,這個free鏈表有一個基礎節點,他會引用鏈表的頭節點和尾節點,裏面還存儲了鏈表中有多少個描述數據塊的節點,也就是有多少個空閒的緩存頁。

3、free鏈表佔用多少內存空間?

可能有的人會以爲這個描述數據塊,在Buffer Pool裏有一份,在free鏈表裏也有一份,好像在內存裏有兩個一模一樣的描述數據塊,是麼?

其實這麼想就大錯特錯了。

這裏要給大家講明白一點,這個free鏈表,他本身其實就是由Buffer Pool裏的描述數據塊組成的,你可以認爲是每個描述數據塊裏都有兩個指針,一個是free_pre,一個是free_next,分別指向自己的上一個free鏈表的節點,以及下一個free鏈表的節點。

通過Buffer Pool中的描述數據塊的free_pre和free_next兩個指針,就可以把所有的描述數據塊串成一個free鏈表,大家可以自己去思考一下這個問題。上面爲了畫圖需要,所以把描述數據塊單獨畫了一份出來,表示他們之間的指針引用關係。

對於free鏈表而言,只有一個基礎節點是不屬於Buffer Pool的,他是40字節大小的一個節點,裏面就存放了free鏈表的頭節點的地址,尾節點的地址,還有free鏈表裏當前有多少個節點。

4、如何將磁盤上的頁讀取到Buffer Pool的緩存頁中去?

好了,現在我們可以來解答這一篇文章的最後一個問題了,當你需要把磁盤上的數據頁讀取到Buffer Pool中的緩存頁裏去的時候,是怎麼做到的?

其實有了free鏈表之後,這個問題就很簡單了。

首先,我們需要從free鏈表裏獲取一個描述數據塊,然後就可以對應的獲取到這個描述數據塊對應的空閒緩存頁,我們看下圖所示。
在這裏插入圖片描述
接着我們就可以把磁盤上的數據頁讀取到對應的緩存頁裏去,同時把相關的一些描述數據寫入緩存頁的描述數據塊裏去,比如這個數據頁所屬的表空間之類的信息,最後把那個描述數據塊從free鏈表裏去除就可以了,如下圖所示。
在這裏插入圖片描述
可能有朋友還是疑惑,這個描述數據塊是怎麼從free鏈表裏移除的呢?

簡單,我給你一段僞代碼演示一下。

假設有一個描述數據塊02,他的上一個節點是描述數據塊01,下一個節點是描述數據塊03,那麼他在內存中的數據結構如下。

在這裏插入圖片描述
現在假設block03被使用了,要從free鏈表中移除,那麼此時直接就可以把block02節點的free_next設置爲null就可以了,block03就從free鏈表裏失去引用關係了,如下所示。

在這裏插入圖片描述
想必看到這裏,大家就完全明白,磁盤中的數據頁是如何讀取到Buffer Pool中的緩存頁裏去的了,而且這個過程中free鏈表是用來幹什麼的。

5、你怎麼知道數據頁有沒有被緩存?

接着我們來看下一個問題:你怎麼知道一個數據頁有沒有被緩存呢?

我們在執行增刪改查的時候,肯定是先看看這個數據頁有沒有被緩存,如果沒被緩存就走上面的邏輯,從free鏈表中找到一個空閒的緩存頁,從磁盤上讀取數據頁寫入緩存頁,寫入描述數據,從free鏈表中移除這個描述數據塊。

但是如果數據頁已經被緩存了,那麼就會直接使用了。

所以其實數據庫還會有一個哈希表數據結構,他會用表空間號+數據頁號,作爲一個key,然後緩存頁的地址作爲value。

當你要使用一個數據頁的時候,通過“表空間號+數據頁號”作爲key去這個哈希表裏查一下,如果沒有就讀取數據頁,如果已經有了,就說明數據頁已經被緩存了。

我們看下圖,又引入了一個數據頁緩存哈希表的結構。

也就是說,每次你讀取一個數據頁到緩存之後,都會在這個哈希表中寫入一個key-value對,key就是表空間號+數據頁號,value就是緩存頁的地址,那麼下次如果你再使用這個數據頁,就可以從哈希表裏直接讀取出來他已經被放入一個緩存頁了。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章