作者:王雨晨
愛可生資料庫工程師,負責 MySQL 日常維護及 DMP 產品支援。
本文來源:原創投稿
*愛可生開源社群出品,原創內容未經授權不得隨意使用,轉載請聯絡小編並註明來源。
問題背景
有使用者在使用 MySQL5.7 的資料庫時,遇到 undo 暴漲情況,經排查存在一條慢 SQL 執行了上萬秒仍沒有結束,導致後續事務產生的 undo 不能清理,越來越多
線上 truncate undo log 已開啟,將慢 SQL kill 掉之後,undo 大小超過 innodb_max_undo_log_size 設定的大小,但 undo 檔案沒有立即收縮
測試驗證
測試引數如下,開啟 innodb_undo_log_truncate
mysql> show variables like '%undo%';
+--------------------------+-----------+
| Variable_name | Value |
+--------------------------+-----------+
| innodb_max_undo_log_size | 104857600 |
| innodb_undo_directory | ./ |
| innodb_undo_log_truncate | ON |
| innodb_undo_logs | 128 |
| innodb_undo_tablespaces | 3 |
+--------------------------+-----------+
5 rows in set (0.00 sec)
模擬 undo 增長,超過 innodb_max_undo_log_size 設定大小
# du -sh ./undo*
152M ./undo001
296M ./undo002
15M ./undo003
檢視官方文件undo清理策略,簡單概括為以下:
1、啟用 innodb_undo_log_truncate 後,超過 innodb_max_undo_log_size 設定大小的undo表空間被標記為截斷
2、被標記的undo表空間的回滾段被設定為不活躍的,不能分配給新的事務
3、purge執行緒釋放不需要的回滾段
4、釋放回滾段後,undo表空間被截斷為初始大小10M
可以看到在收縮undo大小前,需要purge執行緒先釋放回滾段,這裡涉及另一個引數 innodb_purge_rseg_truncate_frequency,預設值128,表示purge執行緒每呼叫128次,就釋放回滾段一次
此次問題背景中,該引數設定的是預設值
mysql> show variables like 'innodb_purge_rseg_truncate_frequency';
+--------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------+-------+
| innodb_purge_rseg_truncate_frequency | 128 |
+--------------------------------------+-------+
1 row in set (0.01 sec)
所以為了儘快收縮 undo 檔案,我們可以將 innodb_purge_rseg_truncate_frequency 值調小,提高 purge 執行緒釋放回滾段的頻率
//調小該值
mysql> show variables like 'innodb_purge_rseg_truncate_frequency';
+--------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------+-------+
| innodb_purge_rseg_truncate_frequency | 16 |
+--------------------------------------+-------+
1 row in set (0.01 sec)
//達到purge執行緒呼叫次數,釋放回滾段,undo表空間被截斷
# du -sh ./undo*
10M ./undo001
10M ./undo002
15M ./undo003
MySQL8.0新增 Manual Truncation
MySQL8.0 新增支援使用 SQL 語句來管理 undo 表空間
1、需要至少三個活躍的 undo 表空間,因為要保證有兩個活躍的 undo 表空間來支援 Automated Truncation
手工建立一個 undo 表空間,必須以 .ibu 結尾
mysql> create undo tablespace undo_003 add datafile '/data/mysql/data/3307/undo_003.ibu';
Query OK, 0 rows affected (0.27 sec)
//三個處於 active 狀態的 undo 表空間
mysql> SELECT NAME, STATE FROM INFORMATION_SCHEMA.INNODB_TABLESPACES WHERE NAME LIKE '%undo%';
+-----------------+--------+
| NAME | STATE |
+-----------------+--------+
| innodb_undo_001 | active |
| innodb_undo_002 | active |
| undo_003 | active |
+-----------------+--------+
3 rows in set (0.00 sec)
2、手工截斷 undo 表空間,需要先將 undo 表空間設定為 inactive
//模擬 undo 增長
# du -sh ./undo*
81M ./undo_001
157M ./undo_002
26M ./undo_003.ibu
mysql> ALTER UNDO TABLESPACE innodb_undo_002 SET INACTIVE;
Query OK, 0 rows affected (0.01 sec)
3、手工設定 inactive 後,undo 表空間被標記為截斷,purge 執行緒會增加返回頻率,快速清空並最終截斷 undo 表空間,狀態變為 empty
mysql> SELECT NAME, STATE FROM INFORMATION_SCHEMA.INNODB_TABLESPACES WHERE NAME LIKE '%undo%';
+-----------------+--------+
| NAME | STATE |
+-----------------+--------+
| innodb_undo_001 | active |
| innodb_undo_002 | empty |
| undo_003 | active |
+-----------------+--------+
//undo 檔案收縮
# du -sh ./undo*
81M ./undo_001
2.1M ./undo_002
26M ./undo_003.ibu
4、empty 狀態的 undo 表空間可以重新啟用使用
mysql> ALTER UNDO TABLESPACE innodb_undo_002 SET ACTIVE;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT NAME, STATE FROM INFORMATION_SCHEMA.INNODB_TABLESPACES WHERE NAME LIKE '%undo%';
+-----------------+--------+
| NAME | STATE |
+-----------------+--------+
| innodb_undo_001 | active |
| innodb_undo_002 | active |
| undo_003 | active |
+-----------------+--------+
3 rows in set (0.01 sec)
5、MySQL8.0 支援刪除表空間,但前提是該表空間為 empty 狀態
mysql> ALTER UNDO TABLESPACE undo_003 SET INACTIVE;
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT NAME, STATE FROM INFORMATION_SCHEMA.INNODB_TABLESPACES WHERE NAME LIKE '%undo%';
+-----------------+--------+
| NAME | STATE |
+-----------------+--------+
| innodb_undo_001 | active |
| innodb_undo_002 | active |
| undo_003 | empty |
+-----------------+--------+
3 rows in set (0.01 sec)
mysql> DROP UNDO TABLESPACE undo_003;
Query OK, 0 rows affected (0.02 sec)
mysql> SELECT TABLESPACE_NAME, FILE_NAME FROM INFORMATION_SCHEMA.FILES WHERE FILE_TYPE LIKE 'UNDO LOG';
+-----------------+------------+
| TABLESPACE_NAME | FILE_NAME |
+-----------------+------------+
| innodb_undo_001 | ./undo_001 |
| innodb_undo_002 | ./undo_002 |
+-----------------+------------+
2 rows in set (0.01 sec)