利用 WAL 技術(shù),數(shù)據(jù)庫將隨機(jī)寫轉(zhuǎn)換成了順序?qū)?,大大提升了?shù)據(jù)庫的性能,由此也帶來了內(nèi)存臟頁的問題。
?臟頁會被后臺線程自動 flush,也會由于數(shù)據(jù)頁淘汰而觸發(fā) flush,而刷臟頁的過程由于會占用資源,可能會讓你的更新和查詢語句的響應(yīng)時間長一些。
?
一、flush 臟頁
當(dāng)內(nèi)存數(shù)據(jù)頁跟磁盤數(shù)據(jù)頁內(nèi)容不一致的時候,我們成這個內(nèi)存頁為“臟頁”;內(nèi)存數(shù)據(jù)寫入磁盤后,內(nèi)存和磁盤上的數(shù)據(jù)頁內(nèi)容就一致了,稱為“干凈頁”。
?文章來源地址http://www.zghlxwxcb.cn/news/detail-409608.html
InnoDB引擎以頁作為磁盤和內(nèi)存之間交互的基本單位,數(shù)據(jù)庫 I/O 操作的最小單位是頁。也就是說,在數(shù)據(jù)庫中,不論讀一行,還是讀多行,都是將這些行所在的頁進(jìn)行加載。
?文章來源:http://www.zghlxwxcb.cn/news/detail-409608.html
?
?
記錄是按照行來存儲的,但是數(shù)據(jù)庫的讀取并不以行為單位,否則一次讀?。ㄒ簿褪且淮?I/O 操作)只能處理一行數(shù)據(jù),效率會非常低。?
Buffer Pool 中存的就是一頁一頁的數(shù)據(jù),當(dāng)我們要查詢的數(shù)據(jù)不在 Buffer Pool 中時,InnoDB 會將記錄所在的頁整個加載到 Buffer Pool 中去。
同樣的,將 Buffer Pool 中的臟頁刷入磁盤時,也是按照頁為單位刷入磁盤的。
??
1、Free List
你從磁盤中讀取一個數(shù)據(jù)頁,會先從Free List中找出一個空閑緩存頁的描述信息,然后將你讀出的數(shù)據(jù)頁中加載進(jìn)緩存頁中。同時將緩存頁的描述信息從Free List中剔除,此外該描述信息塊還會被維護(hù)進(jìn)LRU鏈表中。
數(shù)據(jù)頁被加載進(jìn)Buffer Pool后你就可以對其進(jìn)行變更操作了。
?
3、Flush List
如果我們修改了Buffer Pool中某個緩沖頁的數(shù)據(jù),那么它就與磁盤上的頁不一致了,這樣的緩沖頁也被稱之為臟頁(dirty page)。
為了性能問題,我們每次修改緩沖頁后,并不著急立刻把修改刷新到磁盤上,而是在未來的某個時間點(diǎn)進(jìn)行刷新操作。?
如果有了修復(fù)發(fā)生,不是立刻刷新,那之后再刷新的時,我們怎么知道Buffer Pool中哪些頁是臟頁,哪些頁從來沒有被修改過呢?
?
創(chuàng)建一個存儲臟頁的 Flush list,凡是被修改過的緩沖頁對應(yīng)的控制塊都會作為節(jié)點(diǎn)加入到這個鏈表中。
4、LRU List
除了以上,Buffer Pool還有另外一種LRU List,整體結(jié)構(gòu)如下:
?
?
?
?
在BufferPool中,內(nèi)存管理如下:
- 需要找 free 空閑數(shù)據(jù)塊:free list
- 需要找冷熱訪問的數(shù)據(jù)塊:lru list
- 需要知道哪些數(shù)據(jù)塊是臟的:flush list
?
?
二、刷新方式有哪幾種
1、從flush鏈表中刷新一部分頁面到磁盤
?
后臺線程會根據(jù)當(dāng)時系統(tǒng)的繁忙程度確定刷新速率,定時從flush鏈表中刷新一部分頁面到磁盤,即:BUF_FLUSH_LIST
?有時后臺線程刷新臟頁的進(jìn)度比較慢,導(dǎo)致用戶準(zhǔn)備加載一個磁盤頁到Buffer Pool中時沒有可用的緩沖頁。此時,就會嘗試查看LRU鏈表尾部,看是否存在可以直接釋放掉的未修改緩沖頁。
如果沒有,則不得不將LRU鏈表尾部的一個臟頁同步刷新到磁盤(與磁盤交互是很慢的,這會降低處理用戶請求的速度),即:BUF_FLUSH_SINGLE_PAGE
?
2、從LRU鏈表的冷數(shù)據(jù)中刷新一部分頁面到磁盤,即:BUF_FLUSH_LRU
?
后臺線程會定時從LRU鏈表的尾部開始掃描一些頁面,掃描的頁面數(shù)量可以通過系統(tǒng)變量innodb_lru_scan_depth來指定,如果在LRU鏈表中發(fā)現(xiàn)臟頁,則把它們刷新到磁盤。
控制塊里會存儲該緩沖頁是否被修改的信息,所以在掃描LRU鏈表時,可以很輕松地獲取到某個緩沖頁是否是臟頁的信息。
?
三、flush性能問題
flush臟頁雖然是常態(tài),但是出現(xiàn)以下這兩種情況,都是會明顯影響性能的:
- 一個查詢要淘汰的臟頁個數(shù)太多,會導(dǎo)致查詢的響應(yīng)時間明顯變長;
- 日志寫滿,更新全部堵住,寫性能跌為 0,這種情況對敏感業(yè)務(wù)來說,是不能接受的。
?
InnoDB 會在后臺刷臟頁,而刷臟頁的過程是要將內(nèi)存頁寫入磁盤。所以,無論是你的查詢語句在需要內(nèi)存的時候可能要求淘汰一個臟頁,還是由于刷臟頁的邏輯會占用 IO 資源并可能影響到了你的更新語句,都可能是造成你從業(yè)務(wù)端感知到 MySQL“抖”了一下的原因。
?
要盡量避免這種情況,你就要合理地設(shè)置 innodb_io_capacity 的值,并且平時要多關(guān)注臟頁比例,不要讓它經(jīng)常接近 75%。?
一旦一個查詢請求需要在執(zhí)行過程中先 flush 掉一個臟頁時,這個查詢就可能要比平時慢了。
而 MySQL 中的一個機(jī)制,可能讓你的查詢會更慢:在準(zhǔn)備刷一個臟頁的時候,如果這個數(shù)據(jù)頁旁邊的數(shù)據(jù)頁剛好是臟頁,就會把這個“鄰居”也帶著一起刷掉;而且這個把“鄰居”拖下水的邏輯還可以繼續(xù)蔓延,也就是對于每個鄰居數(shù)據(jù)頁,如果跟它相鄰的數(shù)據(jù)頁也還是臟頁的話,也會被放到一起刷。
?
在 InnoDB 中,innodb_flush_neighbors 參數(shù)就是用來控制這個行為的,值為 1 的時候會有上述的“連坐”機(jī)制,值為 0 時表示不找鄰居,自己刷自己的。
找“鄰居”這個優(yōu)化在機(jī)械硬盤時代是很有意義的,可以減少很多隨機(jī) IO。機(jī)械硬盤的隨機(jī) IOPS 一般只有幾百,相同的邏輯操作減少隨機(jī) IO 就意味著系統(tǒng)性能的大幅度提升。
而如果使用的是 SSD 這類 IOPS 比較高的設(shè)備的話,建議你把 innodb_flush_neighbors 的值設(shè)置成 0。因?yàn)檫@時候 IOPS 往往不是瓶頸,而“只刷自己”,就能更快地執(zhí)行完必要的刷臟頁操作,減少 SQL 語句響應(yīng)時間。
在 MySQL 8.0 中,innodb_flush_neighbors 參數(shù)的默認(rèn)值已經(jīng)是 0 了。
參考資料:
https://hackmysql.com/post/book-6/
《MySQL實(shí)戰(zhàn)45講》
?
?
?
到了這里,關(guān)于InnoDB引擎之flush臟頁的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!