MySQL中undo log介紹及清理

lff1530983327發表於2022-10-10

帶著問題去學習總是事半功倍的,最近在重溫MySQL架構,這裡面不得不又提起兩日誌大將,redo和undo了。所以想著提筆記錄一下也做一個重新的梳理。

開篇問題:

1.show engine innodb status中的history list length是哪種物件的長度?

2.history list length 什麼時候會被清理?

3.history list length 為什麼該值很大的時候,系統多少會有點問題?

4.關於 undo log清理的引數 innodb_purge_batch_size 文件中提過一個128次迭代,這個迭代指的是什麼操作的迭代次數?innodb_purge_batch_size引數設定的清理物件為?

5.history list length 和 待purge的delete marked 物件是什麼關係?

6.MySQL較低版本中出現的ibdata空間很大的問題和undo log有什麼關係?

1.表空間

在MySQL5.6及之前,undo log都是儲存於MySQL系統表空間的,之後才出現了undo log獨立表空間。MySQL8.0.3開始獨立undo表空間的預設個數從0改為2,最大128個。UNDO log的頁型別為FIL_PAGE_UNDO_LOG,這些頁面可以從系統表空間中分配,也可以從undo tablespace中分配。在系統表空間的第5號頁面中有一個8x128的位置存放著128個rollback segment header。

2.段

undo log透過回滾段進行管理,每個undo log tablespace預設128個回滾段,以round-robin的方式進行分配。這128號回滾段可分為2類,第0號、33-127號為一類,對普通表進行的改動產生的undo log則從這類段中分配。1-32號為對臨時表做記錄改動時用到的表空間。0號回滾段必須在系統表空間中(ibdata檔案),第1-32號必須在臨時表空間中(ibtmp1檔案)。做這樣的區分原因主要是,系統重啟後,臨時表的undo表空間是不需要被恢復的,所以不需要記錄undo log頁面修改時的redo log。segement同其他tablespace下的段管理一樣,都是有一些零散頁面以及一些完整的區組成。每個段對應一個rollback segement header頁面,其中存放了1024個undo log 頁面連結串列的first undo page的頁號,可以稱之為 undo slot。rollback segement header中記錄著 undo 頁面的最大值、HISTORY連結串列佔用的頁面數量、HISTORY連結串列的基節點、undo slot集合(頁號)。

3.連結串列

undo log會按照操作型別的不同存放不同的連結串列,主要有INSERT和UPDATE兩大類。除此之外,還會按照普通表操作和臨時表操作存放不同的連結串列,所以一個事務最多會涉及到4條undo連結串列。還有一個概念為 垃圾連結串列 和 版本鏈。

4.頁面

undo頁面由連結串列管理,連結串列的base node存放在第一個undo log 頁面的 undo log segment header中。list node資訊存放在第一個頁面的undo page header中。undo log segement header描述關於段的資訊。主要包括的資訊有:1)本undo頁面連結串列處於什麼狀態(AVCTIVE、CACHED、TO_FREE、TO_PURGE、PREPARE)2)undo頁面連結串列對應的segment資訊 3)基節點資訊。undo log header儲存關於一個事務產生的一組undo log的資訊,包括TRX_ID、XID、TRX_NO、本組日誌第一條日誌的偏移量、是否包括del mark產生的undo log、上/下一組日誌偏移、是否由DDL操作產生、一個12位元組的List Node結構,代表一個稱之為History連結串列的節點。

5.重用

insert 型別的undo頁面可以在事務提交之後被重用(連結串列只包含一個undo log頁面,該頁面已經使用空間小於整個頁面的3/4)。update 型別的undo log頁面在事務提交之後不能被重用,需要用於MVCC。

6.分配

對於一個新的rollback segment header來說,其中的undo slot均為FIN_NULL值,說明該slot不指向任何一個頁面,新事務從第一個slot開始,在表空間中建立一個undo log segment,然後從其中申請一個undo 頁面作為 first undo 頁面,然後將undo slot的值設定為剛剛申請的頁號。如果undo slot對應的連結串列可以被重用則會被加入 insert undo cache連結串列/update cache 連結串列,新事務會優先在cache連結串列中找undo slot,沒有再去找rollback segment header。如果undo slot不符合被重用的條件,insert undo 連結串列會被設定為TO_FREE狀態,update undo連結串列會被設定為TO_PURGE狀態,將undo slot設定為FIN_NULL,然後將本次事務寫入的一組undo 放到HOSTORY連結串列中。

7.清理

當事務提交時,事務對應的回滾段會被加入到purge_queue,該queue是以第一個提交事務的trx_x:no作為key的優先順序佇列,purge_sys:view表示所有全域性檢視連結串列中最老的一個檢視。當trx_x:no < purge_sys:view時候,就可以被清理了。undo log的清理分為兩個階段:undo_purge 和undo_truncate其中undo_purge是清理TRX_UNDO_DEL_MARK類undo對應的record,由引數innodb_purge_batch_size控制每次清理的record數量。在完成一批undo record的清理之後再進行真正的undo log的清理,稱之為 undo_truncate(釋放回滾段),由引數 innodb_purge_rseg_truncate_frequency  控制清理的頻次,預設是128次,之後會進行一次undo tablespace的truncate。

這裡官方文件並未提及到這個128次迭代是 innodb_purge_rseg_truncate_frequency  控制的(?)

8.關於purge階段的undo型別

row_purge_record_func 清理 TRX_UNDO_DEL_MARK_REC 型別的undo record, 記錄被直接標記刪除的,需要物理清理聚簇索引和二級索引,row_purge_upd_exist_or_extern清理TRX_UNDO_UPD_EXIST_REC(不更新主鍵的情況,包括就地更新和先刪除再插入類)該型別的undo record, 聚簇索引被in-place更新但二級索引順序可能發生改變,二級索引更新為 刪除+插入,此時需要根據回滾段去檢查二級索引記錄是否發生改變,並做清理操作。

 undo型別分為insert型別和update型別以及delete型別,其中insert型別主要包括各主鍵列資訊,update中分更新主鍵和不更新主鍵,不更新主鍵如果更新前後記錄大小不變則就地更新,如果大小未改變了則會執行就地更新,undo記錄中記錄更改前的值、主鍵各列資訊和索引各列(包含索引列更改)資訊( TRX_UNDO_UPD_EXIST_REC);如果大小改變了則會進行delete和insert新記錄的方式,這裡的delete是直接delete,而涉及到主鍵更改的update也是先delete後insert,但是這裡的是delete mark( TRX_UNDO_DEL_MARK_REC+ TRX_UNDO_INSERT_REC)。

9.版本差異

在早期版本中(<5.6),undo tablespace是在ibdata中,因此一個常見的問題是由於大事務不提交導致ibdata膨脹,這時候通常只有重建資料庫一途來縮小空間。

到了MySQL5.6版本, InnoDB開始支援獨立的undo tablespace,也就是說,undo log可以儲存於ibdata之外。但是需要install的時候就制定好後續不做更改,並且spaceid 從1開始。

MySQL5.7版本中,引入online truncate undo tablespace。

在MySQL8.0,InnoDB再進一步,對undo log做了進一步的改進:

  1. 無需從space_id 1開始建立undo tablespace,這樣解決了In-place upgrade或者物理恢復到一個開啟了Undo tablespace的例項所產生的space id衝突。不過依然要求undo tablespace的space id是連續分配的( fsp_is_undo_tablespace ), space id的範圍為(0xFFFFFFF0UL - 128, 0xFFFFFFF0UL - 1) (Bug #23517560)

  2. 從8.0.3版本開始,預設undo tablespace的個數從0調整為2,也就是在8.0版本中,獨立undo tablespace被預設開啟。修改該引數為0會報warning並在未來不再支援( WL#10583 )

  3. 允許動態的增加undo tablespace的個數,也就是說可以動態調整innodb_undo_tablespaces。當調大該引數時,會去建立新的undo tablespace。但如果設小該值,則僅僅是不實用多出來的Undo tablespace,目前不會去主動刪除它們( innodb_undo_tablespaces_update ),  WL#9507

  4. Undo tablespace的命名從undoNNN修改為undo_NNN

  5. 和以前版本最大的不同之處就是,在8.0之前只能建立128個回滾段,而在8.0版本開始,每個Undo tablespace可以建立128個回滾段,也就是說,總共有innodb_rollback_segments * innodb_undo_tablespaces個回滾段。這個改變的好處是在高併發下可以顯著的減少因為分配到同一個回滾段內的事務間產生的鎖衝突

  6. Innodb_undo_truncate引數預設開啟,這意味著預設情況下,undo tablespace超過1GB(引數innodb_max_undo_log_size來控制)時,就會觸發online truncate

  7. 支援undo tablespace加密(  參考文件 )

  8. 在ibdata中保留的32個slo t原本用於臨時表空間的回滾段,但事實上他們並沒有做任何的持久化,因此在8.0中直接在記憶體中為其建立單獨的記憶體結構,這32個slot可以用於持久化的undo回滾段()!

10.相關引數

10.1  innodb_undo_log_truncate 

預設1G,truncate後預設10M。

When the  innodb_undo_log_truncate variable is enabled, undo tablespaces that exceed the size limit defined by the  innodb_max_undo_log_size variable are subject to truncation

10.2  innodb_undo_tablespaces 

預設2,最大127,8.0.14後被棄用

A MySQL instance supports up to 127 undo tablespaces including the two default undo tablespaces created when the MySQL instance is initialized.Prior to MySQL 8.0.14, additional undo tablespaces are created by configuring the  innodb_undo_tablespaces startup variable. This variable is deprecated and no longer configurable as of MySQL 8.0.14.

10.3  innodb_undo_log_truncate

預設ON

10.4   innodb_purge_rseg_truncate_frequency 

預設128,每128次purge呼叫就會觸發一次回滾段清理。

Defines the frequency with which the purge system frees rollback segments in terms of the number of times that purge is invoked. Normally, the purge system frees rollback segments once every 128 times that purge is invoked. The default value is 128. Reducing this value increases the frequency with which the purge thread frees rollback segments.


問題解答


參考資料:

MySQL 是怎樣執行的:從根兒上理解 MySQL

https://developer.aliyun.com/article/341036



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30018455/viewspace-2917541/,如需轉載,請註明出處,否則將追究法律責任。

相關文章