17 MySQL是如何基于冷熱數(shù)據(jù)分離的方案,來優(yōu)化LRU算法的?

MySQL是如何基于冷熱數(shù)據(jù)分離的方案,來優(yōu)化LRU算法的?
1、昨日思考題解答
先給大家解答一下上次給大家布置的思考題,上回我們給大家提了一個(gè)問題:為什么MySQL要設(shè)計(jì)一個(gè)預(yù)讀機(jī)制,為什么有時(shí)候要把相鄰的一些數(shù)據(jù)頁一次性讀入到Buffer Pool緩存里去?
道理很簡單,說白了還不是為了提升性能么。假設(shè)你讀取了數(shù)據(jù)頁01到緩存頁里去,那么好,接下來有可能會(huì)接著順序讀取數(shù)據(jù)頁01相鄰的數(shù)據(jù)頁02到緩存頁里去,這個(gè)時(shí)候,是不是可能在讀取數(shù)據(jù)頁02的時(shí)候要再次發(fā)起一次磁盤IO?
所以為了優(yōu)化性能,MySQL才設(shè)計(jì)了預(yù)讀機(jī)制,也就是說如果在一個(gè)區(qū)內(nèi),你順序讀取了好多數(shù)據(jù)頁了,比如數(shù)據(jù)頁01~數(shù)據(jù)頁56都被你依次順序讀取了,MySQL會(huì)判斷,你可能接著會(huì)繼續(xù)順序讀取后面的數(shù)據(jù)頁。
那么此時(shí)他就干脆提前把后續(xù)的一大堆數(shù)據(jù)頁(比如數(shù)據(jù)頁57~數(shù)據(jù)頁72)都讀取到Buffer Pool里去,那么后續(xù)你再讀取數(shù)據(jù)頁60的時(shí)候,是不是就可以直接從Buffer Pool里拿到數(shù)據(jù)了?
當(dāng)然理想是上述那樣,很豐滿,但是現(xiàn)實(shí)可能很骨感。你預(yù)讀的一大堆數(shù)據(jù)頁要是占據(jù)了LRU鏈表的前面部分,可能這些預(yù)讀的數(shù)據(jù)頁壓根兒后續(xù)沒人會(huì)使用,那你這個(gè)預(yù)讀機(jī)制就是在搗亂了。
2、基于冷熱數(shù)據(jù)分離的思想設(shè)計(jì)LRU鏈表
所以為了解決上一講我們說的簡單的LRU鏈表的問題,真正MySQL在設(shè)計(jì)LRU鏈表的時(shí)候,采取的實(shí)際上是冷熱數(shù)據(jù)分離的思想。
之前一系列的問題,說白了,不都是因?yàn)樗芯彺骓摱蓟煸谝粋€(gè)LRU鏈表里,才導(dǎo)致的么?
所以真正的LRU鏈表,會(huì)被拆分為兩個(gè)部分,一部分是熱數(shù)據(jù),一部分是冷數(shù)據(jù),這個(gè)冷熱數(shù)據(jù)的比例是由innodb_old_blocks_pct參數(shù)控制的,他默認(rèn)是37,也就是說冷數(shù)據(jù)占比37%。
這個(gè)時(shí)候,LRU鏈表實(shí)際上看起來是下面這樣子的。
? ? ? ? ? ?

3、數(shù)據(jù)頁第一次被加載到緩存的時(shí)候
好,既然我們知道LRU鏈表已經(jīng)按照一定的比例被拆分為了冷熱兩塊區(qū)域了,那么接下來就來看看在運(yùn)行期間,冷熱兩個(gè)區(qū)域是如何使用的。
首先數(shù)據(jù)頁第一次被加載到緩存的時(shí)候,這個(gè)時(shí)候緩存頁會(huì)被放在LRU鏈表的哪個(gè)位置呢?
實(shí)際上這個(gè)時(shí)候,緩存頁會(huì)被放在冷數(shù)據(jù)區(qū)域的鏈表頭部,我們看下面的圖,也就是第一次把一個(gè)數(shù)據(jù)頁加載到緩存頁之后,這個(gè)緩存頁實(shí)際上是被放在下圖箭頭的位置,也就是冷數(shù)據(jù)區(qū)域的鏈表頭部位置。
? ? ? ? ? ?

? ? ? ?
4、冷數(shù)據(jù)區(qū)域的緩存頁什么時(shí)候會(huì)被放入到熱數(shù)據(jù)區(qū)域?
接著我們來思考一個(gè)問題,第一次被加載了數(shù)據(jù)的緩存頁,都會(huì)不停的移動(dòng)到冷數(shù)據(jù)區(qū)域的鏈表頭部,如上圖所示
那么你要知道,冷數(shù)據(jù)區(qū)域的緩存頁肯定是會(huì)被使用的,那么冷數(shù)據(jù)區(qū)域的緩存頁什么時(shí)候會(huì)放到熱數(shù)據(jù)區(qū)域呢?
實(shí)際上肯定很多人會(huì)想,只要對(duì)冷數(shù)據(jù)區(qū)域的緩存頁進(jìn)行了一次訪問,就立馬把這個(gè)緩存頁放到熱數(shù)據(jù)區(qū)域的頭部行不行呢?如下圖所示。? ?

? ? ? ? ? ? ?
其實(shí)這也是不合理的,如果你剛加載了一個(gè)數(shù)據(jù)頁到那個(gè)緩存頁,他是在冷數(shù)據(jù)區(qū)域的鏈表頭部,然后立馬(在1ms以內(nèi))就訪問了一下這個(gè)緩存頁,之后就再也不訪問他了呢?難道這種情況你也要把那個(gè)緩存頁放到熱數(shù)據(jù)區(qū)域的頭部嗎?
所以MySQL設(shè)定了一個(gè)規(guī)則,他設(shè)計(jì)了一個(gè)innodb_old_blocks_time參數(shù),默認(rèn)值1000,也就是1000毫秒
也就是說,必須是一個(gè)數(shù)據(jù)頁被加載到緩存頁之后,在1s之后,你訪問這個(gè)緩存頁,他才會(huì)被挪動(dòng)到熱數(shù)據(jù)區(qū)域的鏈表頭部去。
因?yàn)榧僭O(shè)你加載了一個(gè)數(shù)據(jù)頁到緩存去,然后過了1s之后你還訪問了這個(gè)緩存頁,說明你后續(xù)很可能會(huì)經(jīng)常要訪問它,這個(gè)時(shí)間限制就是1s,因此只有1s后你訪問了這個(gè)緩存頁,他才會(huì)給你把緩存頁放到熱數(shù)據(jù)區(qū)域的鏈表頭部去。
所以我們看下面的圖,文字說明做了一點(diǎn)改動(dòng),是數(shù)據(jù)加載到緩存頁之后過了1s,你再訪問這個(gè)緩存頁,他就會(huì)被放入熱數(shù)據(jù)區(qū)域的鏈表頭部,如果是你數(shù)據(jù)剛加載到緩存頁,在1s內(nèi)你就訪問緩存頁,此時(shí)他是不會(huì)把這個(gè)緩存頁放入熱數(shù)據(jù)區(qū)域的頭部的。
? ? ??

? ? ? ? ? ? ?
5、思考題
今天給大家留一個(gè)思考題,大家思考一下,現(xiàn)在我們已經(jīng)知道了,數(shù)據(jù)頁第一次被加載到緩存頁之后,這個(gè)緩存頁是放在LRU鏈表的冷數(shù)據(jù)區(qū)域的頭部的,然后必須是1s過后訪問換個(gè)緩存頁,他才會(huì)被移動(dòng)到熱數(shù)據(jù)區(qū)域的鏈表頭部。
好,那么基于這套冷熱數(shù)據(jù)隔離的方案,LRU鏈表的冷數(shù)據(jù)區(qū)域放的都是什么樣的緩存頁?這個(gè)問題有點(diǎn)像腦筋急轉(zhuǎn)彎一樣,大家腦子一轉(zhuǎn),就能思考出來了。
大家可以好好思考一下,把你的答案發(fā)到評(píng)論區(qū)里跟其他同學(xué)交流。
End
專欄版權(quán)歸公眾號(hào)儒猿技術(shù)窩所有
未經(jīng)許可不得傳播,如有侵權(quán)將追究法律責(zé)任