MySQL 偶爾抽風,效能突然下降

2022-01-10 12:56:40 字數 3726 閱讀 5492

有時會碰到這樣的情況,一條 sql 在平時執行沒問題,很快。但是突然某個時間執行的就會很慢,而且這種場景並不能復現,只能隨機傳送的。

在之前講解 mysql redo log 時,說到了 wal 機制,為了保證 mysql 更新的速度,在進行更新操作時,先將更新內容寫入 redo log,後續系統空閒時,再將 redo log 的內容應用到磁碟。

當記憶體資料頁(redo log)和磁碟資料頁內容不一致時,將該記憶體也稱為 「髒頁」。將記憶體資料寫入到磁碟後,資料一致,記憶體頁稱為 "乾淨頁"。

在記憶體資料寫入磁碟時,這個過程稱為 flush 過程。sql 突然執行變得很慢,效能下降。原因就可能和 flush 操作有關。

因為在進行 flush 操作時,更新操作會等待 redo log 的寫入。

場景一:redo log 日誌已經記滿。這時系統會停止更新操作,將 check point 向前推進,讓 redo log 留出空間可以繼續寫。

這裡假設 cp 到 cp『 間隙已經寫入到磁碟,這部分就變成了乾淨頁,此時 write pos 就可以寫入這部分區域了。

場景二:系統記憶體不足,需要新的記憶體頁時,發現記憶體不夠用了,就需要淘汰一些資料頁。如果淘汰時,這時資料頁時髒頁,就要將髒頁寫到磁碟。

這時有個問題是,命名 redo log 中的內容已經被記錄到日誌中了,假如記憶體滿了,直接刪除不就可以嗎?下次讀入時,再把 redo log 日誌中的內容應用到磁碟。

沒有選擇直接清空記憶體,是從效能考慮的,因為在查詢資料時,有兩種情況:

所以這樣效率比較高。

場景三:mysql 會在系統空閒時,進入 flush 操作。

場景四:在 mysql 正常關閉時,會把記憶體髒頁 flush 到磁碟上。

對於第三,四場景來說,是比較正常的情況,不需要考慮效能問題。

對於第一種場景,innodb 會盡量避免,因為在這種情況下,整個系統不再接受更新。

但有時出現人為的配置錯誤,比如記憶體為 128 gb,innodb_io_capacity 設定為 20000 的例項。通常建議將 redo log 設定成 4 個 1gb 的檔案。但由於配置錯誤,設定成 100m 的檔案。

這裡由於 redo log 設定的太小,很快就會被寫滿。write pos 一直追著 check point. 這時,系統只能停止所有更新,推進 checkpoint.

表現就是,磁碟 io 很小,但是出現間歇性的效能下降。

對於第二種場景,記憶體不夠用的情況,innodb 會用緩衝池(buffer pool)管理記憶體

記憶體頁在緩衝池中會有三種狀態:

每個資料頁頭部有lsn,8位元組,每次修改都會變大。

對比這個 lsn 跟 checkpoint 的 lsn,比checkpoint小的一定是乾淨頁

由於 innodb 的策略是盡可能使用記憶體,所以對於長時間執行的庫來說,未被使用的頁面很少。

當發現想讀入的資料頁沒有在記憶體中時,必須到緩衝池申請資料頁。並會把最久不用得資料頁從記憶體中淘汰

當時,如果在下面的情況進行刷髒頁,會明顯影響效能:

為了解決這個問題,innodb 使用控制髒頁比例的機制,來避免上面的情況。

在 innodb 中,通過innodb_io_capacity引數,來告訴 innodb 目前主機的磁碟能力是多少,這個值建議設定成磁碟的 iops.

可以通過 fio 這個工具來測試:

fio -filename=$filename -direct=1 -iodepth 1 -thread -rw=randrw -ioengine=psync -bs=16k -size=500m -numjobs=10 -runtime=10 -group_reporting -name=mytest
由於innodb_io_capacity導致的效能問題很常見,比如有時系統吞吐量(tps)很低,寫入很慢,但是磁碟 io 並不高。就有可能是該引數設定的不正確。例如,innodb_io_capacity的值設定的很低,但是磁碟用的 ssd,導致 inoodb 認為系統能力很差,所以刷髒頁特別慢。造成髒頁累計,影響查詢和更新效能。

innodb 在刷盤時主要考慮兩個因素:

髒頁的比例

redo log 寫盤速度

會通過這兩個因素單獨先算出兩個數字。

innodb_max_dirty_pages_pct髒頁比例上限,預設 75%.

innodb會根據髒頁的比例(m),算出範圍在 0 - 100 的數字。,過程稱為 f1(m)

# m 髒頁比例

f1(m)

除此之外,innodb 每次寫入日誌都會有乙個序號 n. 然後根據 n 再算出乙個 0 到 100 的數字,這個計算過程稱為 f2(n)

n: 當前寫入的序號和 checkpoint 對應序號之間的差值。

最後,根據 f1(m)和 f2(n)兩個值,取其中較大的值為 r,之後引擎就可以按照innodb_io_capacity* r 來控制刷髒頁的速度。

所以無論是在查詢,需要載入資料到記憶體資料頁,而淘汰髒頁。還是更新時,導致刷盤操作都有可能造成 mysql 的效能下降。

為了避免這種情況,要合理的設定innodb_io_capacity的值,平時要多關注髒頁比例,不讓其接近 75%.

其中髒頁比例可以通過下面的方式獲取:

mysql> use performance_schema;

mysql> select variable_value into @a from global_status where variable_name = 'innodb_buffer_pool_pages_dirty';

select variable_value into @b from global_status where variable_name = 'innodb_buffer_pool_pages_total';

select @a/@b;

初次之外,在乙個查詢操作進行時,如果需要 flush 髒頁的話,如果這個該髒頁的鄰居也是髒頁的話,就會把這個鄰居一起刷掉,如果恰好旁邊還是髒頁的話,就會一直連坐。這時導致 flush 過慢的原因。

可以通過innodb_flush_neighbors來控制該行為,值為 1 開啟上述機制,為 0 則關閉。

對於機械硬碟來說,是可以減少很多隨機 io ,因為機械硬碟 iops 一般就幾百,減少隨機 io 就意味著效能提公升。

但如果用 ssd 這類 iops 較高的裝置,iops 往往不是瓶頸,關閉就好,減少 sql 語句的響應時間。

8.0中,已經預設是 0 了.

這篇中,主要介紹了 wal 時的 flush 操作可能會造成 mysql 突然的效能下降。

引起的原因一般是由於記憶體不夠導致的,進而可以通過設定合適的innodb_io_capacity引數,來控制 innodb flush 的過程。

丁奇 mysql 45 講鏈結

mysql資料變少了 MySQL效能突然下降的原因

有時會碰到這樣的情況,一條 sql 在平時執行沒問題,很快。但是突然某個時間執行的就會很慢,而且這種場景並不能復現,只能隨機傳送的。sql 執行突然變慢的原因 在之前講解 mysql redo log 時,說到了 wal 機制,為了保證 mysql 更新的速度,在進行更新操作時,先將更新內容寫入 r...

mysql 重新整理 效能下降的原因 四

什麼是效能下降?其實就是 執行的環境變了,那麼環境變化是什麼?比如cpu上公升了,記憶體滿了。有或者表中數量增加了,量變了。其實這些是dba幹的,但是呢,我們也需要去了解下,並且優化我們的code。簡單介紹乙個量大的情況,那麼這個時候我們可能會建立索引,其實也不是量大去建立索引,而是量大且查詢資料多...

Oracle遷移到MySQL效能下降的注意點

背景 最近有較多的客戶系統由原來由oracle改造到mysql後出現了效能問題cpu 100 或是後台的crm系統複雜sql在業務高峰的時候出現堆積導致業務故障。在我的記憶裡面 最初從oracle遷移到mysql期間也遇到了很多sql的效能問題,記憶最為深刻的子查詢,當初的版本是mysql5.1,這...