繼上兩期月報,mysql5.7新特性之一介紹了一些新特性及相容性問題,mysql 5.7新特性之二介紹了臨時表的優化和實作。
這期我們一起來學習下undo空間管理,重點介紹truncate功能。
innodb存儲引擎中,undo在完成事務復原和mvcc之後,就可以purge掉了,但undo在事務執行過程中,進行的空間配置設定如何回收,就變成了一個問題。 我們親曆使用者的小執行個體,因為一個大事務,導緻ibdata file到800g大小。
我們先大緻看下innodb的undo在不同的版本上的一些演進:
mysql 5.5的版本上
innodb undo是放在系統表空間即ibdata file檔案中,這樣如果有比較大的事務(即需要生成大量undo的),會撐大ibdata資料檔案,
雖然空間可以重用, 但檔案大小不能更改。
關于復原段的,隻有這個主要的參數,用來設定多少個rollback segment。
mysql 5.6的版本上
innodb undo支援獨立表空間, 增加如下參數:
這樣,在install的時候,就會在data目錄下增加undo資料檔案,來組成undo獨立表空間,但檔案變大之後的空間回收還是成為問題。
mysql 5.7的版本上
innodb undo在支援獨立表空間的基礎上,支援表空間的truncate功能,增加了如下參數:
innodb的purge線程,會根據innodb_undo_log_truncate開關的設定,和innodb_max_undo_log_size設定的檔案大小門檻值,以及truncate的頻率來進行空間回收和rollback segment的重新初始化。
接下來我們詳細看下5.7的innodb undo的管理:
設定innodb_undo_tablespaces的個數, 在mysql install的時候,建立指定數量的表空間。
innodb支援128個undo logs,這裡特别說明下,從5.7開始,innodb_rollback_segments的名字改成了innodb_undo_logs,但表示的都是復原段的個數。
從5.7.2開始,其中32個undo logs為臨時表的事務配置設定的,因為這部分undo不記錄redo,不需要recovery,另外從33-128一共96個是redo-enabled undo。
rollback segment的配置設定如下:
其中如果是臨時表的事務,需要配置設定兩個undo logs,其中一個是non-redo undo logs;這部分用于臨時表資料的復原。
另外一個是redo-enabled undo log,是為臨時表的中繼資料準備的,需要recovery。
而且, 其中32個rollback segment建立在臨時表空間中,并且臨時表空間中的復原段在每次server start的時候,需要重建。
每一個rollback segment可以配置設定1024個slot,也就是可以支援96*1024個并發的事務同時, 但如果是臨時表的事務,需要占用兩個slot。
innodb undo的空間管理簡圖如下:
注核心結構說明:
1. rseg slot
rseg slot一共128個,儲存在ibdata系統表空間中,其位置在:
每一個slot儲存着rollback segment header的位置。包括space_id + page_no,占用8個bytes。其宏定義:
2. rseg header
rseg header在undo表空間中,每一個rseg包括1024個undo segment slot,每一個slot儲存着undo segment header的位置,包括page_no,暫用4個bytes,因為undo segment不會跨表空間,是以space_id就沒有必要了。
其宏定義如下:
3. undo segment header
undo segment header page即段内的第一個undo page,其中包括四個比較重要的結構:
undo segment header
進行段内空間的管理
undo page header
page内空間的管理,page的類型:fil_page_undo_log
undo header
包含undo record的連結清單,以便安裝事務的反順序,進行復原
undo record
剩下的就是undo記錄了。
undo段的配置設定比較簡單,其過程如下:
首先是rollback segment的配置設定:
使用round-robin的方式來配置設定rollback segment
如果有單獨設定undo表空間,就不使用system表空間中的undo segment
如果設定的是truncate的就不配置設定
一旦配置設定了,就設定trx_ref_count,不允許truncate。
具體代碼參考:
其次是undo segment的建立:
從rollback segment裡邊選擇一個free的slot,如果沒有,就會報錯,通常是并發的事務太多。
錯誤日志如下:
如果有free,就建立一個undo的segment。
核心的代碼如下:
undo的truncate主要由下面兩個參數控制:innodb_purge_rseg_truncate_frequency,innodb_undo_log_truncate。
1. innodb_undo_log_truncate是開關參數。
2. innodb_purge_rseg_truncate_frequency預設128,表示purge undo輪詢128次後,進行一次undo的truncate。
當設定innodb_undo_log_truncate=on的時候, undo表空間的檔案大小,如果超過了innodb_max_undo_log_size, 就會被truncate到初始大小,但有一個前提,就是表空間中的undo不再被使用。
其主要步驟如下:
1. 超過大小了之後,會被mark truncation,一次會選擇一個
2. 選擇的undo不能再配置設定新給新的事務
3. purge線程清理不再需要的rollback segment
4. 等所有的復原段都釋放了後,truncate操作,使其成為install db時的初始狀态。
預設情況下, 是purge觸發128次之後,進行一次rollback segment的free操作,然後如果全部free就進行一個truncate。
但mark的操作需要幾個依賴條件需要滿足:
1. 系統至少得有兩個undo表空間,防止一個offline後,至少另外一個還能工作
2. 除了ibdata裡的segment,還至少有兩個segment可用
3. undo表空間的大小确實超過了設定的門檻值
其核心代碼參考:
因為,隻要你設定了truncate = on,mysql就盡可能的幫你去truncate所有的undo表空間,是以它會循環的把undo表空間加入到mark清單中。
最後,循環所有的undo段,如果所屬的表空間是marked truncate,就把這個rseg标志位不可配置設定,加入到trunc隊列中,在purge的時候,進行free rollback segment。
注意:
如果是線上庫,要注意影響,因為當一個undo tablespace在進行truncate的時候,不再承擔undo的配置設定。隻能由剩下的undo 表空間的rollback segment接受事務undo空間請求。
mysql 5.7 新特性系列,下次進行group replication的分享,敬請期待。