20 對(duì)于LRU鏈表中尾部的緩存頁,是如何淘汰他們刷入磁盤的?

對(duì)于LRU鏈表中尾部的緩存頁,是如何淘汰他們刷入磁盤的?
1、Buffer Pool的緩存頁以及幾個(gè)鏈表的使用回顧
接著我們來講講,你的Buffer Pool在運(yùn)行中被使用的時(shí)候,實(shí)際上會(huì)頻繁的從磁盤上加載數(shù)據(jù)頁到他的緩存頁里去,然后free鏈表、flush鏈表、lru鏈表都會(huì)在使用的時(shí)候同時(shí)被使用
比如數(shù)據(jù)加載到一個(gè)緩存頁,free鏈表里會(huì)移除這個(gè)緩存頁,然后lru鏈表的冷數(shù)據(jù)區(qū)域的頭部會(huì)放入這個(gè)緩存頁。
然后如果你要是修改了一個(gè)緩存頁,那么flush鏈表中會(huì)記錄這個(gè)臟頁,lru鏈表中還可能會(huì)把你從冷數(shù)據(jù)區(qū)域移動(dòng)到熱數(shù)據(jù)區(qū)域的頭部去。
如果你是查詢了一個(gè)緩存頁,那么此時(shí)就會(huì)把這個(gè)緩存頁在lru鏈表中移動(dòng)到熱數(shù)據(jù)區(qū)域去,或者在熱數(shù)據(jù)區(qū)域中也有可能會(huì)移動(dòng)到頭部去。
總之,MySQL在執(zhí)行CRUD的時(shí)候,首先就是大量的操作緩存頁以及對(duì)應(yīng)的幾個(gè)鏈表。然后在緩存頁都滿的時(shí)候,必然要想辦法把一些緩存頁給刷入磁盤,然后清空這幾個(gè)緩存頁,接著把需要的數(shù)據(jù)頁加載到緩存頁里去!
我們已經(jīng)知道,他是根據(jù)LRU鏈表去淘汰緩存頁的,那么他到底是什么時(shí)候把LRU鏈表的冷數(shù)據(jù)區(qū)域中的緩存頁刷入磁盤的呢?實(shí)際上他有幾個(gè)時(shí)機(jī)。
2、定時(shí)把LRU尾部的部分緩存頁刷入磁盤
首先第一個(gè)時(shí)機(jī),并不是在緩存頁滿的時(shí)候,才會(huì)挑選LRU冷數(shù)據(jù)區(qū)域尾部的幾個(gè)緩存頁刷入磁盤,而是有一個(gè)后臺(tái)線程,他會(huì)運(yùn)行一個(gè)定時(shí)任務(wù),這個(gè)定時(shí)任務(wù)每隔一段時(shí)間就會(huì)把LRU鏈表的冷數(shù)據(jù)區(qū)域的尾部的一些緩存頁,刷入磁盤里去,清空這幾個(gè)緩存頁,把他們加入回free鏈表去!
所以實(shí)際上在緩存頁沒用完的時(shí)候,可能就會(huì)清空一些緩存頁了,我們看下面的圖示。
? ? ? ? ? ?

所以大家會(huì)發(fā)現(xiàn),只要有這個(gè)后臺(tái)線程定時(shí)運(yùn)行,可能你的緩存頁都沒用完呢,人家就給你把一批冷數(shù)據(jù)的緩存頁刷入磁盤,清空出來一批緩存頁,那么你就多了一批可以使用的空閑緩存頁了!
所以如果在一個(gè)動(dòng)態(tài)的運(yùn)行效果中思考,大概就是你不停的加載數(shù)據(jù)到一些空閑的緩存頁里去,然后這些緩存頁可能被使用,會(huì)在lru鏈表中各種移動(dòng)。然后同時(shí)有一個(gè)后臺(tái)線程還不停的把冷數(shù)據(jù)區(qū)域的一些不用的緩存頁刷入磁盤中,清空一些緩存頁出來。
只要有緩存頁被刷人磁盤,大家可以想象一下,那么這個(gè)緩存頁必然會(huì)加入到free鏈表中,從flush鏈表中移除,從lru鏈表中移除。
3、把flush鏈表中的一些緩存頁定時(shí)刷入磁盤
如果僅僅是把LRU鏈表中的冷數(shù)據(jù)區(qū)域的緩存頁刷入磁盤,大家覺得夠嗎?
明顯不夠啊,因?yàn)樵趌ru鏈表的熱數(shù)據(jù)區(qū)域里的很多緩存頁可能也會(huì)被頻繁的修改,難道他們永遠(yuǎn)都不刷入磁盤中了嗎?
所以這個(gè)后臺(tái)線程同時(shí)也會(huì)在MySQL不怎么繁忙的時(shí)候,找個(gè)時(shí)間把flush鏈表中的緩存頁都刷入磁盤中,這樣被你修改過的數(shù)據(jù),遲早都會(huì)刷入磁盤的!
只要flush鏈表中的一波緩存頁被刷入了磁盤,那么這些緩存頁也會(huì)從flush鏈表和lru鏈表中移除,然后加入到free鏈表中去!
所以你可以理解為,你一邊不停的加載數(shù)據(jù)到緩存頁里去,不停的查詢和修改緩存數(shù)據(jù),然后free鏈表中的緩存頁不停的在減少,flush鏈表中的緩存頁不停的在增加,lru鏈表中的緩存頁不停的在增加和移動(dòng)。
另外一邊,你的后臺(tái)線程不停的在把lru鏈表的冷數(shù)據(jù)區(qū)域的緩存頁以及flush鏈表的緩存頁,刷入磁盤中來清空緩存頁,然后flush鏈表和lru鏈表中的緩存頁在減少,free鏈表中的緩存頁在增加。
這就是一個(gè)動(dòng)態(tài)運(yùn)行起來的效果!
4、實(shí)在沒有空閑緩存頁了怎么辦?
那么實(shí)在沒有空閑緩存頁了怎么辦呢?
此時(shí)可能所有的free鏈表都被使用了,然后flush鏈表中有一大堆被修改過的緩存頁,lru鏈表中有一大堆的緩存頁,根據(jù)冷熱數(shù)據(jù)進(jìn)行了分離,大致是如此的效果。
這個(gè)時(shí)候如果要從磁盤加載數(shù)據(jù)頁到一個(gè)空閑緩存頁中,此時(shí)就會(huì)從LRU鏈表的冷數(shù)據(jù)區(qū)域的尾部找到一個(gè)緩存頁,他一定是最不經(jīng)常使用的緩存頁!然后把他刷入磁盤和清空,然后把數(shù)據(jù)頁加載到這個(gè)騰出來的空閑緩存頁里去!
這就是MySQL的Buffer Pool緩存機(jī)制的一整套運(yùn)行原理!我們已經(jīng)完整的講完了緩存頁的加載和使用,以及free鏈表、flush鏈表、lru鏈表是怎么使用的,包括緩存頁是如何刷入磁盤騰出來空閑緩存頁的,以及緩存頁沒有空閑的時(shí)候應(yīng)該怎么處理。
大家首先理解了最近幾篇文章之后,就應(yīng)該完全理解了,MySQL在執(zhí)行CRUD操作的時(shí)候,是如何盡可能基于內(nèi)存中的緩存來處理的。
5、今日思考題
今天我們來給大家一個(gè)思考題,大家發(fā)現(xiàn)沒有,如果你在執(zhí)行CRUD的時(shí)候要從磁盤加載數(shù)據(jù)頁到Buffer Pool的緩存頁的時(shí)候,一旦此時(shí)沒有空閑的緩存頁,就必須從LRU鏈表的冷數(shù)據(jù)區(qū)域的尾部把一個(gè)緩存頁刷入磁盤,然后騰出來一個(gè)空閑的緩存頁,接著你才能基于緩存數(shù)據(jù)來執(zhí)行這個(gè)CRUD的操作。
但是如果頻繁的出現(xiàn)這樣的一個(gè)情況,那你的很多CRUD執(zhí)行的時(shí)候,難道都要先刷一個(gè)緩存頁到磁盤上去?然后再從磁盤上讀取一個(gè)數(shù)據(jù)頁到空閑的緩存頁里來?這樣豈不是每次CRUD操作都要執(zhí)行兩次磁盤IO?那么性能豈不是會(huì)極差?
所以我們來思考一個(gè)問題:你的MySQL的內(nèi)核參數(shù),應(yīng)該如何優(yōu)化,優(yōu)化哪些地方的行為,才能夠盡可能的避免在執(zhí)行CRUD的時(shí)候,經(jīng)常要先刷一個(gè)緩存頁到磁盤上去,才能讀取一個(gè)磁盤上的數(shù)據(jù)頁到空閑緩存頁里來?
End
專欄版權(quán)歸公眾號(hào)儒猿技術(shù)窩所有
未經(jīng)許可不得傳播,如有侵權(quán)將追究法律責(zé)任