天天看點

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

transaction history增長情況

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

MySQL · 特性分析 · InnoDB transaction history

innodb purge 情況

顯示在并發 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增長最常見的兩種場景:

大查詢

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

mysql重新開機

即使transaction history并沒有急劇增加,但mysql重新開機操作,buffer pool的重新預熱,還是導緻purge變成io密集型操作。不過mysql 5.6提供了innodb buffer pool的dump和reload方法,可以顯著減輕purge的io壓力。

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

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

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

innodb提供了兩個參數<code>innodb_max_purge_lag</code>,<code>innodb_max_purge_lag_delay</code> 來調整,即當<code>trx_sys-&gt;rseg_history_len</code>超過了設定的<code>innodb_max_purge_lag</code>,就影響dml操作最大delay不超過<code>innodb_max_purge_lag_delay</code>設定的時間,以microseconds來計算。

其核心計算代碼如下:

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

缺陷1:針對total history length

假設transaction history中保留兩類records,一類是是馬上可以被purge的,一類是因為active transaction而不能purge的。但大多數時間,我們期望的是purgable history比較小,而不是整個history。

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

<code>trx_sys-&gt;rseg_history_len</code>是一個目前history的長度,而不是一個interval時間段内undo的增長和減少的變化情況,導緻<code>trx_sys-&gt;rseg_history_len</code>一旦超過<code>innodb_max_purge_lag</code>這個設定的值,就對dml産生不超過<code>innodb_max_purge_lag_delay</code>的時間delay,一旦低于這個值馬上delay 時間就又恢複成 0。

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

MySQL · 特性分析 · InnoDB transaction history

purge 造成系統抖動

針對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增加帶來的問題呢?

監控

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

調整參數

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

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

purge完之後再shutdown

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

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

預熱

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