平時的工做中,不知道你有沒有遇到過這樣的場景,一條 SQL 語句,正常執行的時候特別快,可是有時也不知道怎麼回事,它就會變得特別慢,而且這樣的場景很難復現,它不僅隨機,並且持續時間還很短。數據庫
當內存數據頁跟磁盤數據頁內容不一致的時候,咱們稱這個內存頁爲「髒頁」。內存數據寫入到磁盤後,內存和磁盤上的數據頁的內容就一致了,稱爲「乾淨頁」。性能
平時執行很快的更新操做,其實就是在寫內存和日誌,而 MySQL 偶爾「抖」一下的那個瞬間,可能就是在刷髒頁(flush)。spa
那麼,什麼狀況會引起數據庫的 flush 過程呢?日誌
第一種場景是InnoDB 的 redo log 寫滿了,這時候系統會中止全部更新操做,把 checkpoint 往前推動,redo log 留出空間能夠繼續寫。內存
第二種場景對應的就是系統內存不足。當須要新的內存頁,而內存不夠用的時候,就要淘汰一些數據頁,空出內存給別的數據頁使用。若是淘汰的是「髒頁」,就要先將髒頁寫到磁盤。ci
第三種場景對應的就是 MySQL 認爲系統「空閒」的時候。固然,MySQL忙起來但是會很快就能把redo log 記滿的,因此要合理地安排時間,即便是忙的時候,也要見縫插針地找時間,只要有機會就刷一點「髒頁」。資源
第四種場景對應的就是 MySQL 正常關閉的狀況。這時候,MySQL 會把內存的髒頁都 flush 到磁盤上,這樣下次 MySQL 啓動的時候,就能夠直接從磁盤上讀數據,啓動速度會很快。it
接下來,你能夠分析一下上面四種場景對性能的影響。io
其中,第三種狀況是屬於 MySQL 空閒時的操做,這時系統沒什麼壓力,而第四種場景是數據庫原本就要關閉了。這兩種狀況下,你不會太關注「性能」問題。因此這裏,咱們主要來分析一下前兩種場景下的性能問題。innodb
第一種是「redo log 寫滿了,要 flush 髒頁」,這種狀況是 InnoDB 要儘可能避免的。由於出現這種狀況的時候,整個系統就不能再接受更新了,全部的更新都必須堵住。若是你從監控上看,這時候更新數會跌爲 0。
第二種是「內存不夠用了,要先將髒頁寫到磁盤」,這種狀況實際上是常態。InnoDB 用緩衝池(buffer pool)管理內存,緩衝池中的內存頁有三種狀態:
InnoDB 的策略是儘可能使用內存,所以對於一個長時間運行的庫來講,未被使用的頁面不多。
而當要讀入的數據頁沒有在內存的時候,就必須到緩衝池中申請一個數據頁。這時候只能把最久不使用的數據頁從內存中淘汰掉:若是要淘汰的是一個乾淨頁,就直接釋放出來複用;但若是是髒頁呢,就必須將髒頁先刷到磁盤,變成乾淨頁後才能複用。
因此,刷髒頁雖然是常態,可是出現如下這兩種狀況,都是會明顯影響性能的:
一個查詢要淘汰的髒頁個數太多,會致使查詢的響應時間明顯變長;
日誌寫滿,更新所有堵住,寫性能跌爲 0,這種狀況對敏感業務來講,是不能接受的。
因此,InnoDB 須要有控制髒頁比例的機制,來儘可能避免上面的這兩種狀況。
InnoDB 刷髒頁的控制策略
首先,你要正確地告訴 InnoDB 所在主機的 IO 能力,這樣 InnoDB 才能知道須要全力刷髒頁的時候,能夠刷多快。
這就要用到 innodb_io_capacity 這個參數了,它會告訴 InnoDB 你的磁盤能力。
如今你知道了,InnoDB 會在後臺刷髒頁,而刷髒頁的過程是要將內存頁寫入磁盤。因此,不管是你的查詢語句在須要內存的時候可能要求淘汰一個髒頁,仍是因爲刷髒頁的邏輯會佔用 IO 資源並可能影響到了你的更新語句,均可能是形成你從業務端感知到 MySQL「抖」了一下的緣由。
要儘可能避免這種狀況,你就要合理地設置 innodb_io_capacity 的值,而且平時要多關注髒頁比例(參數 innodb_max_dirty_pages_pct),不要讓它常常接近 75%。