【云圖說】第235期 DDS讀寫兩步走 帶您領(lǐng)略只讀節(jié)點的風采
723
2025-03-31
若BP緩存頁不夠了,咋辦?
執(zhí)行CRUD都會將磁盤數(shù)據(jù)頁加載到緩存頁,那在加載數(shù)據(jù)到緩存頁時,必然是要加載到空閑緩存頁,所以必須要從free中找個空閑緩存頁,然后把磁盤數(shù)據(jù)頁加載到該空閑緩存頁
隨著不斷將磁盤數(shù)據(jù)頁加載到空閑緩存頁,free中的空閑緩存頁會越來越少。最終耗盡free中的空閑緩存頁。這時,還要加載數(shù)據(jù)頁到一個空閑緩存頁時,MySQL 該何去何從?
若所有緩存頁都有數(shù)據(jù)了,那就無法再從磁盤加載新數(shù)據(jù)頁到緩存頁了,則只能淘汰一些緩存頁:把一個緩存頁里被修改過的數(shù)據(jù),刷到磁盤的數(shù)據(jù)頁,然后該緩存頁就能被清空, 變回空閑頁。然后就能將磁盤的新數(shù)據(jù)頁加載到這剛騰出的空閑頁:
那應(yīng)該把哪個倒霉的緩存頁的數(shù)據(jù)刷盤呢?
緩存命中率
現(xiàn)有兩個緩存頁:
一個緩存頁的數(shù)據(jù),經(jīng)常被修改和查詢,都可以操作緩存,不需要從磁盤加載數(shù)據(jù),這那緩存命中率就很高。這種高級員工就是啥臟活累活,都會接受。
另一個緩存頁里的數(shù)據(jù),剛從磁盤加載到緩存頁后,被修改和查詢過1次,之后100次請求再沒有一次是修改和查詢該緩存頁數(shù)據(jù)的,那這緩存命中率就有點低了,因為大部分請求還是走磁盤查詢數(shù)據(jù),他們要操作的數(shù)據(jù)不在緩存。這種高級員工啥事都干不了,都還得交給低級員工們干事。
很顯然,作為領(lǐng)導(dǎo)的你,肯定想把第二個員工裁了吧?
引入LRU,判斷哪些緩存頁不常用
如何知曉哪些緩存頁經(jīng)常被訪問,哪些緩存頁很少被訪問?
這就需要LRU,Least Recently Used,最近最少使用。這樣當緩存頁需空出一個刷盤時,通過LRU鏈表,就能知道最近最少被使用的緩存頁。
LRU工作原理
假設(shè)從磁盤加載一個數(shù)據(jù)頁到緩存頁時,就將該緩存頁的描述信息塊放入LRU鏈表頭部,那么只要有數(shù)據(jù)的緩存頁,他都會在LRU里,最近被加載數(shù)據(jù)的緩存頁,都會放到LRU鏈表頭部。
假設(shè)某緩存頁的描述信息塊本在LRU鏈尾,后續(xù)你只要查詢或修改了該緩存頁數(shù)據(jù),也要將這緩存頁移到LRU鏈頭,即最近被訪問過的緩存頁,一定在LRU鏈頭。
如此,當無空閑緩存頁時候,就能輕易找出最近最少被訪問的緩存頁去刷盤,即LRU鏈尾的緩存頁,將其刷盤,然后把你需要的磁盤數(shù)據(jù)頁加載到這剛空出的緩存頁。
表和行等概念和表空間、數(shù)據(jù)頁的關(guān)系
表、列和行,都是邏輯概念,我們只關(guān)注DB里有一個表,表里有幾個字段,有多少行,但是這些表里的數(shù)據(jù)
表空間、數(shù)據(jù)頁等是物理概念,在物理層面,表里的數(shù)據(jù)都放在一個表空間,表空間由一堆磁盤上的數(shù)據(jù)文件組成,這些數(shù)據(jù)文件里都存放了表中的數(shù)據(jù),這些數(shù)據(jù)由一個個數(shù)據(jù)頁組織起來
但這樣的LRU實際運行時會有問題。
預(yù)讀
當你從磁盤加載一個數(shù)據(jù)頁時,他可 能會連帶著把該數(shù)據(jù)頁相鄰的其他數(shù)據(jù)頁,也加載到緩存。
現(xiàn)有兩個空閑緩存頁,加載一個數(shù)據(jù)頁時,連帶著把他的一個相鄰數(shù)據(jù)頁也加載到緩存,正好每個數(shù)據(jù)頁放入一個空閑緩存頁!
然后呢?實際上只有一個緩存頁被訪問,另外一個通過預(yù)讀機制加載的緩存頁,其實無人問津,此時這倆緩存頁可都在LRU鏈表前邊:
這時,若無空閑頁了,要加載新數(shù)據(jù)頁,就得從LRU鏈表的尾部將“最近最少使用的緩存頁”取出,刷入磁盤,就空出一個緩存頁了。
若選擇將上圖中LRU尾部那個緩存頁刷盤,然后清空,合理嗎?
他可是之前一直頻繁被訪問呀,只是這一瞬間,被新加載進的兩個緩存頁給占了LRU鏈表前面的位置,尤是第二個緩存頁,居然還是通過預(yù)讀加載來的,其實根本無人訪問!而這時將LRU鏈表尾部緩存頁刷盤,肯定不合理,最合理的反而是將那LRU鏈表第二個通過預(yù)讀機制加載進的緩存頁給淘汰。
MySQL預(yù)讀觸發(fā)時機
參數(shù)innodb_read_ahead_threshold默認56,即若順序訪問了一個區(qū)里的多個數(shù)據(jù)頁,訪問的數(shù)據(jù)頁數(shù)量超過閾值,就會觸發(fā)預(yù)讀,將下個相鄰區(qū)中的所有數(shù)據(jù)頁都加載到緩 存
若BP里緩存了一個區(qū)里的13個連續(xù)的數(shù)據(jù)頁,而且這些數(shù)據(jù)頁都是比較頻繁會被訪問的,此時直接觸發(fā)預(yù)讀,把這個區(qū)里的其他的數(shù)據(jù)頁都加載到緩存里去。該機制通過參數(shù)innodb_random_read_ahead控制,默認OFF關(guān)閉。
所以默認主要第一個規(guī)則可能觸發(fā)預(yù)讀,一下將很多相鄰區(qū)里的數(shù)據(jù)頁加載進緩存,這些緩存頁若突然都放在LRU鏈表前面,且他們其實并沒啥人訪問,就會如上圖,導(dǎo)致本就在緩存里的一些頻繁被訪問的緩存頁卻在LRU鏈尾。后續(xù)一旦要淘汰緩存頁,就會將鏈尾的一些頻繁被訪問的緩存頁給淘汰!
全表掃描
如:
SELECT * FROM xxx
一下子就將表里所有數(shù)據(jù)頁都從磁盤加載到BP。這時他可能會一下子就把這個表的所有數(shù)據(jù)頁都裝入各緩存頁。此時可能LRU鏈表中排在前面的一大串緩存頁,都是全表掃描加載進來的。若此次全表掃描后,后續(xù)幾乎沒用到這個表里的數(shù)據(jù)呢?此時LRU鏈尾可能全都是之前一直被頻繁訪問的那些緩存頁!
然后當需要淘汰緩存頁時,就會將LRU鏈表尾部一直被頻繁訪問的緩存頁給淘汰掉了,而留下之前全表掃描加載進來的大量的不經(jīng)常訪問的緩存頁。
為何MySQL設(shè)計預(yù)讀機制,為何有時要把相鄰的一些數(shù)據(jù)頁一次性讀入到Buffer Pool緩存?
為提升性能。假設(shè)你讀取了數(shù)據(jù)頁01到緩存頁里去,那接下來有可能會接著順序讀取數(shù)據(jù)頁01相鄰的數(shù)據(jù)頁02到緩存頁,是不是可能在讀取數(shù)據(jù)頁02的時候要再次發(fā)起一次磁盤IO?
所以為優(yōu)化性能,MySQL設(shè)計了預(yù)讀機制,即若在一個區(qū)內(nèi),你順序讀取了好多數(shù)據(jù)頁,比如數(shù)據(jù)頁01~56都被你依次順序讀取了,MySQL覺得你可能接著會繼續(xù)順序讀取后面的數(shù)據(jù)頁。
此時他干脆提前把后續(xù)一大堆數(shù)據(jù)頁(如數(shù)據(jù)頁57~72)都讀取到Buffer Pool,后續(xù)你再讀取數(shù)據(jù)頁60時,就能直接從Buffer Pool里拿到。
但現(xiàn)實骨感,預(yù)讀的一大堆數(shù)據(jù)頁要是占據(jù)LRU鏈表前面部分,然而可能這些預(yù)讀的數(shù)據(jù)頁壓根兒后續(xù)無人用,那這預(yù)讀機制對性能不增反減。
冷熱分離的LRU
于是,為了解決前面的問題,真正MySQL采取冷熱數(shù)據(jù)分離思想改良了 LRU。
之前問題都是因為所有緩存頁都混在一個LRU鏈表才導(dǎo)致的,改良版LRU鏈表拆為熱數(shù)據(jù)、冷數(shù)據(jù)兩部分,冷熱數(shù)據(jù)比例由innodb_old_blocks_pct參數(shù)控制,默認37,即冷數(shù)據(jù)占37%。這時的LRU鏈表:
數(shù)據(jù)頁第一次被加載到緩存時,緩存頁會被放在冷區(qū)的鏈表頭部。
冷區(qū)緩存頁何時放入熱區(qū)?
第一次被加載了數(shù)據(jù)的緩存頁都會不停移動到冷區(qū)的鏈表頭部。那為何不放到熱區(qū)頭部呢?
你剛加載了一個數(shù)據(jù)頁到那個緩存頁,他在冷區(qū)的鏈表頭部,然后立馬(在1ms以內(nèi))就又被訪問了,但之后就再也不訪問了呢?難道這種情況也要把這緩存頁放到熱區(qū)頭部嗎?
所以MySQL設(shè)innodb_old_blocks_time參數(shù),默認1000,即1000ms:一個數(shù)據(jù)頁被加載到緩存頁之后,在1s后,你又訪問了該緩存頁,他才會被移到熱區(qū)的鏈表頭部。
MySQL 數(shù)據(jù)庫
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔相應(yīng)法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔相應(yīng)法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。