天天看點

MySQL · 特性分析 · InnoDB transaction history

在寫壓力負載比較重的mysql執行個體上, innodb可能積累了較長的沒有被purge掉的transaction history,導緻執行個體性能的衰減,或者空閑空間被耗盡,下面就來看看它是怎麼産生的,或者有沒有什麼方法來減輕,避免這樣的問題出現。

innodb是一個事務引擎,實作了mvcc特性,也就是在存儲引擎裡對行資料儲存了多個版本。在對行資料進行delete或者update更改時,行資料的前映像會保留一段時間,直到可以被删除的時候。

在大部分oltp負載情況下,前映像會在資料操作完成後的數秒鐘内被删除掉,但在一些情況下,假設存在一些持續很長時間的事務

需要看到資料的前映像,那麼老版本的資料就會被保留相當長一段時間。

雖然mysql 5.6版本增加了多個purge threads來加快完成老版本資料的清理工作,但在write-intensive workload情況下,不一定完全湊效。

peter zaitsev使用sysbench的update進行的測試,無論是innodb_purge_threads=1還是8的時候,顯示的transaction history快速增長的情況,如下圖所示:

MySQL · 特性分析 · InnoDB transaction history

下面看一下同步測試過程中purge的速度(可以通過i_s.innodb_metrics進行查詢):

MySQL · 特性分析 · InnoDB transaction history

顯示在并發process的過程中,purge thread其實處在饑餓狀态,待sysbench結束,purge線程滿載運作清理工作。

說明:

對于peter zaitsev的測試,其實主要是為了說明transaction history的情況,如果是用sysbench進行小事務的oltp測試,并不會産生這麼明顯的transaction history增長而purge thread跟不上的情況,或者他在測試的時候,對sbtest表進行了全表查詢吧,或者設定了rr級别,不過這隻是猜測。

另外一點:

對于undo page大部分被cache在buffer pool的情況下,purge thread還是比較快的,但如果因為buffer pool的不足而導緻undo page被淘汰到disk上的情況,purge操作就會被受限io情況,而導緻跟不上。

我們來看下出現transaction history增長最常見的兩種場景:

1. 大查詢

如果你在一張大表上發起一個長時間運作的查詢,比如mysqldump,那麼purge線程必須停下來等待查詢結束,這個時候transaction undo就會累積。如果buffer pool中 free page緊張,undo page還會被置換到disk上,加劇purge的代價。

2. mysql重新開機

即使transaction history并沒有急劇增加,但mysql重新開機操作,buffer pool的重新預熱,還是導緻purge變成io密集型操作。

不過mysql 5.6提供了innodb buffer pool的dump和reload方法,可以顯著減輕purge的io壓力。

這裡介紹一下如何檢視buffer pool中undo page的cache情況,percona的版本上提供了i_s.innodb_rseg記錄undo的配置設定和使用情況:

從這兩個information_schema下的兩張表可以看到:undo space使用的總大小是1.7g,而buffer pool中cached不足100m。

設計如下場景:

在一定的寫壓力情況下,并發進行一些大查詢,transaction history就會因為undo log無法purge而一直增加。

innodb提供了兩個參數innodb_max_purge_lag,innodb_max_purge_lag_delay 來調整,即當trx_sys->rseg_history_len超過了

設定的innodb_max_purge_lag,就影響dml操作最大delay不超過innodb_max_purge_lag_delay設定的時間,以microseconds來計算。

其核心計算代碼如下:

但這兩個參數設計有明顯的兩個缺陷:

缺陷1:針對total history length

假設transaction history中保留兩類records,一類是是馬上可以被purge的,一類是因為active transaction而不能purge的。

但大多數時間,我們期望的是purgable history比較小,而不是整個history。

缺陷2:針對大小而非變化

trx_sys->rseg_history_len是一個目前history的長度,而不是一個interval時間段内undo的增長和減少的變化情況,

導緻trx_sys->rseg_history_len一旦超過innodb_max_purge_lag這個設定的值,就對dml産生不超過innodb_max_purge_lag_delay的時間delay, 一旦低于這個值馬上delay 時間就又恢複成0。

在對系統的吞吐監控的時候,會發現系統抖動非常厲害,而不是一個平滑的曲線。類似于下圖:

MySQL · 特性分析 · InnoDB transaction history

針對innodb的purge功能,可以從以下幾個因素來綜合考慮:

增加預設purge thread的個數

測量purgable history長度而不是總的長度

針對變化進行調整delay數值,以應對shrinking

基于undo space的大小,而不是事務的個數

調整undo page在buffer pool中的緩存政策,類似insert buffer。

針對undo page使用和index page不同的預讀政策。

以上6條可以針對purge線程進行一些改良。

在目前的mysql 5.6版本上,我們能做哪些調整或者調優方法,以減少transaction history增加帶來的問題。

1. 監控

監控trx_sys的innodb_history_list_length,為它設定報警值,及時關注和處理。

2. 調整參數

如果你的執行個體是寫壓力比較大的話,調整innodb_purge_threads=8,增加并發purge線程數。

謹慎調整innodb_max_purge_lag和innodb_max_purge_lag_delay參數,依據現在的設計,可能你的執行個體的吞吐量會急劇的下降。

3. purge完之後再shutdown

大部分的case下,mysql執行個體重新開機後,會發現purge的性能更差,因為undo page未命中的原因,并且是random io請求。

如果是正常shutdown,就等purge完成再shutdown。

如果是crash,就啟動後等purge完成再接受業務請求。

4. 預熱

使用mysql 5.6 提供的innodb_buffer_pool_dump_at_shutdown=on 和 innodb_buffer_pool_load_at_startup=on進行預熱,把undo space page預熱到buffer pool中。