一條SQL語句,正常執(zhí)行時候特別快,有時候會突然變得特別慢,而且很難復(fù)現(xiàn),它不只是隨機(jī)而且持續(xù)時間很短。 看上去像數(shù)據(jù)庫抖了一下 – 原因就是MySQL在刷臟頁到磁盤。
當(dāng)內(nèi)存數(shù)據(jù)頁和磁盤數(shù)據(jù)頁內(nèi)容不一致的時候,這個數(shù)據(jù)頁被稱為“臟頁”。內(nèi)存數(shù)據(jù)寫入磁盤后,內(nèi)存和磁盤的數(shù)據(jù)頁的內(nèi)容就一致了,稱為“干凈頁”。 不論臟頁還是干凈頁,都存在內(nèi)存里。
觸發(fā)數(shù)據(jù)庫的刷臟頁時機(jī)
InnoDB的redo log寫滿了。這時候系統(tǒng)會停止所有更新操作去刷盤。這種情況應(yīng)該盡量避免,因為出現(xiàn)這種情況的時候,整個系統(tǒng)就不能再接受更新,所有更新唄堵住。
內(nèi)存不足。當(dāng)需要新的內(nèi)存頁,內(nèi)存不夠用的時候,就要淘汰一些數(shù)據(jù)頁,空出內(nèi)存給別的數(shù)據(jù)頁使用。如果淘汰的是“臟頁”,就要將臟頁寫到磁盤。
InnoDB用緩沖池buffer pool來管理內(nèi)存,緩沖池中的內(nèi)存頁有三種狀態(tài):
- 還沒使用
- 使用了并且是干凈頁
- 使用了并且是臟頁
InnoDB的策略是盡量使用內(nèi)存,因此對于一個長時間運行的庫來說,未被使用的頁面很少。當(dāng)要讀入的數(shù)據(jù)頁沒有在內(nèi)存的時候,就必須到緩沖池中申請一個數(shù)據(jù)頁。這時候只能把最久不使用的數(shù)據(jù)頁從內(nèi)存中淘汰掉:如果要淘汰的是一個干凈頁,就直接釋放出來復(fù)用;當(dāng)如果是臟頁,就必須先講臟頁刷盤,變成干凈頁后才能復(fù)用。所以刷臟頁是常態(tài)。
但是出現(xiàn)以下情況會明顯影響性能:
- 一個查詢要淘汰的臟頁個數(shù)太多,就會導(dǎo)致查詢的響應(yīng)事件明顯變長。
- 日志寫滿,更新全部堵住,寫性能跌為0,這種對于敏感業(yè)務(wù)來說,是不能接受的。
- 系統(tǒng)空閑時候。
- MySQL正常關(guān)閉時候。
刷盤策略
InnoDB 需要有控制臟頁比例的機(jī)制,來盡量避免性能的影響。
-
設(shè)置InnoDB所在主機(jī)的IO能力,這樣InnoDB可以獲知全力刷臟頁可以多快。
| innodb_io_capacity = IOPS
case: 如果出現(xiàn)MySQL寫入速度很慢,TPS很低,而數(shù)據(jù)庫主機(jī)IO壓力并不大,很可能就是這個原因。
- InnoDB控制引擎按照“全力”的百分比刷臟頁 - 刷盤速度。
參考
https://time.geekbang.org/column/article/71806
https://gsmtoday.github.io/2019/02/08/flush/