概念:
臟頁(yè):
當(dāng)內(nèi)存數(shù)據(jù)頁(yè)跟磁盤(pán)數(shù)據(jù)頁(yè)不一致時(shí),稱(chēng)為內(nèi)存頁(yè)的“臟頁(yè)”。
干凈頁(yè):
內(nèi)存數(shù)據(jù)寫(xiě)入磁盤(pán)后,內(nèi)存和磁盤(pán)上的數(shù)據(jù)頁(yè)內(nèi)容就一致了,稱(chēng)為“干凈頁(yè)”。
在寫(xiě)內(nèi)存和日志,而MySQL偶爾抖一下的瞬間,可能就是在刷臟頁(yè)(flush)。
什么情況會(huì)引發(fā)數(shù)據(jù)庫(kù)的 flush 過(guò)程呢?
1.?,對(duì)應(yīng)的就是 InnoDB 的 redo log 寫(xiě)滿了。這時(shí)候系統(tǒng)會(huì)停止所有更新操作,把 checkpoint 往前推進(jìn),redo log 留出空間可以繼續(xù)寫(xiě)。留出來(lái)的空間對(duì)應(yīng)的臟頁(yè)都flush到磁盤(pán)中。
2. 對(duì)應(yīng)的就是系統(tǒng)內(nèi)存不足。當(dāng)需要新的內(nèi)存頁(yè),而內(nèi)存不夠用的時(shí)候,就要淘汰一些數(shù)據(jù)頁(yè),空出內(nèi)存給別的數(shù)據(jù)頁(yè)使用。如果淘汰的是“臟頁(yè)”,就要先將臟頁(yè)寫(xiě)到磁盤(pán)。
3.?對(duì)應(yīng)的就是 MySQL 認(rèn)為系統(tǒng)“空閑”的時(shí)候。
4.?對(duì)應(yīng)的就是 MySQL 正常關(guān)閉的情況。這時(shí)候,MySQL 會(huì)把內(nèi)存的臟頁(yè)都flush 到磁盤(pán)上,這樣下次 MySQL 啟動(dòng)的時(shí)候,就可以直接從磁盤(pán)上讀數(shù)據(jù),啟動(dòng)速度會(huì)很快。
上面四種場(chǎng)景對(duì)性能的影響?
1.?第三種情況是屬于 MySQL 空閑時(shí)的操作,這時(shí)系統(tǒng)沒(méi)什么壓力,而第四種場(chǎng)景是數(shù)據(jù)庫(kù)本來(lái)就要關(guān)閉了。這兩種情況下,你不會(huì)太關(guān)注“性能”問(wèn)題。
2.?是“redo log 寫(xiě)滿了,要 flush 臟頁(yè)”,這種情況是 InnoDB 要盡量避免的。因?yàn)槌霈F(xiàn)這種情況的時(shí)候,整個(gè)系統(tǒng)就不能再接受更新了,所有的更新都必須堵住。如果你從監(jiān)控上看,這時(shí)候更新數(shù)會(huì)跌為 0。
3.?“內(nèi)存不夠用了,要先將臟頁(yè)寫(xiě)到磁盤(pán)”,這種情況其實(shí)是常態(tài)。InnoDB 用緩沖池(buffer pool)管理內(nèi)存,緩沖池中的內(nèi)存頁(yè)有三種狀態(tài):
第一種是,還沒(méi)有使用的;
第二種是,使用了并且是干凈頁(yè);
第三種是,使用了并且是臟頁(yè)。
而當(dāng)要讀入的數(shù)據(jù)頁(yè)沒(méi)有在內(nèi)存的時(shí)候,就必須到緩沖池中申請(qǐng)一個(gè)數(shù)據(jù)頁(yè)。這時(shí)候只能把最久不使用的數(shù)據(jù)頁(yè)從內(nèi)存中淘汰掉:如果要淘汰的是一個(gè)干凈頁(yè),就直接釋放出來(lái)復(fù)用;但如果是臟頁(yè)呢,就必須將臟頁(yè)先刷到磁盤(pán),變成干凈頁(yè)后才能復(fù)用。
刷臟頁(yè)雖然是常態(tài),但是出現(xiàn)以下這兩種情況,都是會(huì)明顯影響性能的:
1. 一個(gè)查詢要淘汰的臟頁(yè)個(gè)數(shù)太多,會(huì)導(dǎo)致查詢的響應(yīng)時(shí)間明顯變長(zhǎng);
2. 日志寫(xiě)滿,更新全部堵住,寫(xiě)性能跌為 0,這種情況對(duì)敏感業(yè)務(wù)來(lái)說(shuō),是不能接受的。
InnoDB 刷臟頁(yè)的控制策略
你要正確地告訴 InnoDB 所在主機(jī)的 IO 能力,這樣 InnoDB 才能知道需要全力刷臟頁(yè)的時(shí)候,可以刷多快。
就要用到 innodb_io_capacity 這個(gè)參數(shù)了,它會(huì)告訴 InnoDB 你的磁盤(pán)能力。這個(gè)值建議你設(shè)置成磁盤(pán)的 IOPS。磁盤(pán)的 IOPS 可以通過(guò) fio 這個(gè)工具來(lái)測(cè)試。
InnoDB 的刷盤(pán)速度就是要參考這兩個(gè)因素:
1.?臟頁(yè)比例
2.? redo log 寫(xiě)盤(pán)速度。
參數(shù) innodb_max_dirty_pages_pct 是臟頁(yè)比例上限,默認(rèn)值是 75%。
要盡量避免這種情況,你就要合理地設(shè)置 innodb_io_capacity 的值,并且平時(shí)要多關(guān)注臟頁(yè)比例,不要讓它經(jīng)常接近 75%。
臟頁(yè)比例是通過(guò)
Innodb_buffer_pool_pages_dirty/Innodb_buffer_pool_pages_total 得到的。
MySQL 中的一個(gè)機(jī)制,對(duì)于每個(gè)鄰居數(shù)據(jù)頁(yè),如果跟它相鄰的數(shù)據(jù)頁(yè)也還是臟頁(yè)的話,也會(huì)被放到一起刷。
在 InnoDB 中,innodb_flush_neighbors 參數(shù)就是用來(lái)控制這個(gè)行為的,值為 1 的時(shí)候會(huì)有上述的“連坐”機(jī)制,值為 0 時(shí)表示不找鄰居,自己刷自己的。
如果使用的是 SSD 這類(lèi) IOPS 比較高的設(shè)備的話,就建議你把innodb_flush_neighbors 的值設(shè)置成 0。因?yàn)檫@時(shí)候 IOPS 往往不是瓶頸,而“只刷自己”,就能更快地執(zhí)行完必要的刷臟頁(yè)操作,減少 SQL 語(yǔ)句響應(yīng)時(shí)間。