mysql效能抖動原因

2021-10-24 00:26:51 字數 4191 閱讀 2294

之前文章介紹了 wal 機制。現在你知道了,innodb 在處理更新語句的時候,只做了寫日誌這乙個磁碟操作。這個日誌叫作 redo log(重做日誌)。

當記憶體資料頁跟磁碟資料頁內容不一致的時候,我們稱這個記憶體頁為「髒頁」。記憶體資料寫入到磁碟後,記憶體和磁碟上的資料頁的內容就一致了,稱為「乾淨頁」。

平時執行很快的更新操作,其實就是在寫記憶體和日誌,而 mysql 偶爾「抖」一下的那個瞬間,可能就是在刷髒頁(flush)。

什麼情況會引發資料庫的 flush 過程呢?

第三種場景是,mysql 認為系統「空閒」的時候。

第四種場景是,mysql 正常關閉的情況。這時候,mysql 會把記憶體的髒頁都 flush 到磁碟上,這樣下次 mysql 啟動的時候,就可以直接從磁碟上讀資料,啟動速度會很快。

其中,第三種情況是屬於 mysql 空閒時的操作,這時系統沒什麼壓力,而第四種場景是資料庫本來就要關閉了。這兩種情況下,你不會太關注「效能」問題。所以這裡,我們主要來分析一下前兩種場景下的效能問題。

第一種是「redo log 寫滿了,要 flush 髒頁」,這種情況是 innodb要盡量避免的。因為出現這種情況的時候,整個系統就不能再接受更新了,所有的更新都必須堵住。如果你從監控上看,這時候更新數會跌為 0。

第二種是「記憶體不夠用了,要先將髒頁寫到磁碟」,這種情況其實是常態。innodb 用緩衝池(buffer pool)管理記憶體,緩衝池中的記憶體頁有三種狀態:

innodb 的策略是盡量使用記憶體,因此對於乙個長時間執行的庫來說,未被使用的頁面很少。

而當要讀入的資料頁沒有在記憶體的時候,就必須到緩衝池中申請乙個資料頁。這時候只能把最久不使用的資料頁從記憶體中淘汰掉:如果要淘汰的是乙個乾淨頁,就直接釋放出來復用;但如果是髒頁呢,就必須將髒頁先刷到磁碟,變成乾淨頁後才能復用。

所以,刷髒頁雖然是常態,但是出現以下這兩種情況,都是會明顯影響效能的:

乙個查詢要淘汰的髒頁個數太多,會導致查詢的響應時間明顯變長;

日誌寫滿,更新全部堵住,寫效能跌為 0,這種情況對敏感業務來說,是不能接受的。

所以,innodb 需要有控制髒頁比例的機制,來盡量避免上面的這兩種情況。

首先,你要正確地告訴innodb 所在主機的 io 能力,這樣 innodb 才能知道需要全力刷髒頁的時候,可以刷多快。

這就要用到innodb_io_capacity這個引數了,它會告訴 innodb 你的磁碟能力。這個值我建議你設定成磁碟的 iops。磁碟的 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 引數,而導致的效能問題也比比皆是。

雖然我們現在已經定義了「全力刷髒頁」的行為,但平時總不能一直是全力刷吧?畢竟磁碟能力不能只用來刷髒頁,還需要服務使用者請求。所以接下來,我們就一起看看 innodb 怎麼控制引擎按照「全力」的百分比來刷髒頁。

這個問題可以這麼想,如果刷太慢,會出現什麼情況?首先是記憶體髒頁太多,其次是 redo log 寫滿。

引數innodb_max_dirty_pages_pct是髒頁比例上限,預設值是 75%。innodb 會根據當前的髒頁比例(假設為 m),算出乙個範圍在 0 到 100 之間的數字,計算這個數字的偽**類似這樣:

f1(m)

innodb 每次寫入的日誌都有乙個序號,當前寫入的序號跟 checkpoint 對應的序號之間的差值,我們假設為 n。innodb 會根據這個 n 算出乙個範圍在 0 到 100 之間的數字,這個計算公式可以記為 f2(n)。f2(n) 演算法比較複雜,你只要知道 n 越大,算出來的值越大就好了。

然後,根據上述算得的 f1(m) 和 f2(n) 兩個值,取其中較大的值記為 r,之後引擎就可以按照 innodb_io_capacity 定義的能力乘以 r% 來控制刷髒頁的速度

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 掉乙個髒頁時,這個查詢就可能要比平時慢了。而 mysql 中的乙個機制,可能讓你的查詢會更慢:在準備刷乙個髒頁的時候,如果這個資料頁旁邊的資料頁剛好是髒頁,就會把這個「鄰居」也帶著一起刷掉;而且這個把「鄰居」拖下水的邏輯還可以繼續蔓延,也就是對於每個鄰居資料頁,如果跟它相鄰的資料頁也還是髒頁的話,也會被放到一起刷。

在 innodb 中,innodb_flush_neighbors 引數就是用來控制這個行為的,值為 1 的時候會有上述的「連坐」機制,值為 0 時表示不找鄰居,自己刷自己的。

找「鄰居」這個優化在機械硬碟時代是很有意義的,可以減少很多隨機 io。機械硬碟的隨機 iops 一般只有幾百,相同的邏輯操作減少隨機 io 就意味著系統效能的大幅度提公升。而如果使用的是 ssd 這類 iops 比較高的裝置的話,我就建議你把 innodb_flush_neighbors 的值設定成 0。

在 mysql 8.0 中,innodb_flush_neighbors 引數的預設值已經是 0 了。

當記憶體不夠用了,要將髒頁寫到磁碟,會有乙個資料頁淘汰機制(最久不使用),假設淘汰的是髒頁,則此時髒頁所對應的redo log的位置是隨機的,當有多個不同的髒頁需要刷,則對應的redo log可能在不同的位置,這樣就需要把redo log的多個不同位置刷掉,這樣對於redo log的處理不是就會很麻煩嗎?(合併間隙,移動位置?)

另外,redo log的優勢在於將磁碟隨機寫轉換成了順序寫,如果需要將redo log的不同部分刷掉(刷髒頁),不是就在redo log裡隨機讀寫了麼?

其實由於淘汰的時候,刷髒頁過程不用動redo log檔案的。

這個有個額外的保證,是redo log在「重放」的時候,如果乙個資料頁已經是刷過的,會識別出來並跳過。

那個說fio命令會破壞硬碟的兄弟,是沒用對命令。估計把-filename=/dev/sdb1 。。。這個的意思是從 分割槽 sdb1 的第乙個扇區開始寫入隨機資料,去判斷這個磁碟的寫入速度。如果指定路徑+檔名就不會出這事了比如文中給的例子

寫文章的時候,我還故意用變數,這樣直接拷貝會出錯,然後自己再寫個路徑,已經考慮了安全了?

很多測試人員再做壓力測試的時候 出現剛開始 insert update 很快 一會 就出現很慢,並且延遲很大,大部分是因為redo log 設定太小 引起的,完美詮釋

常見的誤用場景

關於innodb_io_capacity引數,

innodb首先通過innodb_io_capacity引數了解你磁碟的io能力,該值建議設定成磁碟的write iops。當這個值設定較小的時候,innodb認為你的磁碟很慢,刷髒頻率就會較高,來保證記憶體中的髒頁比例較少,當這個值設定較大的時候,innodb認為你的磁碟io能力較強,刷髒頻率就會降低,進而出現抖動的概率就會少很多。 不知道這個理解是否正確

「innodb認為你的磁碟很慢,刷髒頻率就會較高,來保證記憶體中的髒頁比例較少」

相反哦,如果配置得小,刷得就慢的

用fio測了一下,iops 在1500左右,mysql的innodb_io_capacity的預設值為200,但還有乙個引數是innodb_io_capacity_max的引數,值是2000。這種情況下,系統算正常嗎?max引數是怎麼算出來的?

max只是用來控制 innodb_io_capacity的值不能設定超過這個值,真正生效的還是innodb_io_capacity

mysql服務抖動 12 mysql效能抖動

12 mysql效能抖動 sql語句為什麼變 慢 了 在介紹wal機制時,innodb在處理更新語句的時候,只做了寫日誌這乙個磁碟操作,就是redo log,在更新記憶體寫完redo log之後,就返回客戶端成功。當記憶體資料頁和磁碟資料頁內容不一致的時候,稱這個記憶體頁為 髒頁 記憶體資料寫入磁碟...

MySQL儲存寫入效能嚴重抖動分析

案例描述 通過iostat發現儲存的寫效能長期維持在10mb左右,而且因為寫效能差已經導致資料庫效能變差 兩個小時以後,iostat發現系統的寫效能已經能夠到100mb以上,資料庫效能也恢復正常。也就是說,在對系統 資料庫監控中,出現了效能波谷,儲存寫入效能嚴重抖動,為什麼?一 原理過程 由上原理圖...

MySQL儲存寫入效能嚴重抖動分析

案例描述 通過iostat發現儲存的寫效能長期維持在10mb左右,而且因為寫效能差已經導致資料庫效能變差 兩個小時以後,iostat發現系統的寫效能已經能夠到100mb以上,資料庫效能也恢復正常。也就是說,在對系統 資料庫監控中,出現了效能波谷,儲存寫入效能嚴重抖動,為什麼?一 原理過程 由上原理圖...