MySQL抖動的原因

MySQL抖動的原因

我們在使用MySQL實現業務處理的時候,更多的關注可能在SQL本身上面是不是最優的。今天我們從一個很小的點去看一下MySQL的實現原理,那就是MySQL刷髒頁相關的問題。

刷髒頁

在MySQL中,如果內存數據頁(buffer pool)和磁盤數據不一致時,這個內存數據頁我們認為是“髒頁”;當內存數據頁 flush 到磁盤之後,內存數據頁和磁盤數據一致時,此時這個內存數據頁就稱為“乾淨頁”。(內存中的數據和磁盤一致就可以認為是乾淨頁)刷髒頁的過程可以參見如下所示:

MySQL抖動的原因

從上圖可以看出,更新數據時,內存更新完成+redo log 寫完之後就算成功了。但是此時內存中是存在髒頁的,也就是數據還沒有同步到磁盤上。第二個過程就是將內存中的數據同步到磁盤上面。

觸發刷髒頁的條件

  • redo log 寫滿了。因為 redo log 是一塊固定大小的磁盤空間,當redo log 寫滿之後,會強制觸發磁盤的髒頁 flush 。
  • 系統內存不足。因為數據的更新會更新 內存頁+redo log,此時有一個空間不足都會觸發髒頁 flush。
  • 系統負載不高時觸發。也就是系統空閒時可以 flush 一些髒頁,以應對後續可能出現的高負載。
  • MySQL正常關閉。數據庫正常關閉時,此時所有的數據肯定需要寫入磁盤持久化。

從上面的幾個條件我們可以看出,對於MySQL 中的更新數據,主要有以下 2 種狀態:1、內存中有(可能是髒頁或者乾淨頁)數據,此時更新操作只需要更新內存 + 寫入 redo log;2、內存中無對應的數據,此時就需要從磁盤中讀取對應的數據,然後更新內存 + 寫入 redo log。

對於上面可能出現MySQL抖動的條件,我們主要看前面 2 種。

對於第一種,當 redo log 寫滿的時候,MySQL就會拒絕服務,此時不會接受任何更新操作。因此這種情況是 MySQL 所需要避免的。

對於第二種,就是內存不夠用了,這種情況出現的概率還是比較大的。InnoDB 使用 Buffer Pool 管理內存,對於 Buffer Pool 中的內存主要有以下幾種狀態:未被使用;已使用且是乾淨頁;已使用且是髒頁。

因此,當內存不足時,如果要申請一個內存頁時,此時就需要淘汰一個最久不使用的內存頁:如果是“乾淨頁”,則直接釋放出來使用;如果是“髒頁”則首先需要 flush 然後再釋放使用。

總結以上,MySQL發生抖動的原因主要有以下:

  • 內存不足時,所需要淘汰的內存頁(髒頁)太多;
  • redo log 寫滿時,這種可能出現瞬間的拒絕服務。

InnoDB的 flush 髒頁策略

innodb_io_capacity 參數,它會告訴 InnoDB 你的磁盤讀寫能力是多少。這個參數建議設置成機器的 IOPS ,可以通過 fio 工具來測試獲得。如下所示:

$ fio -filename=data.mp4 -direct=1 -iodepth 1 -thread -rw=randrw -ioengine=psync -bs=16k -size=500M -numjobs=10 -runtime=10 -group_reporting -name=mytest

測試完成之後如下圖所示:

MySQL抖動的原因

如果 innodb_io_capacity 設置的過小,比如比正常刷髒頁的速度還慢,則肯定影響正常的IO 讀寫。

  • innodb_max_dirty_pages_pct 參數: 髒頁的比例,默認是 75%。
  • innodb_flush_neighbors 參數:刷髒頁的時候,如果相鄰的內存頁也是髒頁的話,也會flush。這個參數在機械硬盤時代,可能會減少隨機IO 的開銷。使用 SSD 的話建議設置成 0 ,即關閉。

參考:《極客時間:MySQL實戰》、《高性能MySQL》


分享到:


相關文章: