MySQL 核心模塊揭秘 | 事務(wù)提交了,Undo 日志怎么辦?
目錄
- 1. 修改 Insert Undo 段狀態(tài)
- 2. 生成事務(wù)提交號
- 3. 回滾段加入 purge 隊列
- 4. 處理 Update Undo 段
4.1 Undo 段狀態(tài)
4.2 Undo 日志頁數(shù)量
4.3 Undo 日志組
4.4 Undo 段鏈表
- 5. 清理 Insert Undo 段
- 6. 總結(jié)
1. 修改 Insert Undo 段狀態(tài)
事務(wù)提交過程中,首先要處理的 Undo 相關(guān)邏輯,就是修改 Insert Undo 段的狀態(tài):
- 如果事務(wù)分配了用戶普通表 Insert Undo 段,修改該 Undo 段的狀態(tài)。
- 如果事務(wù)分配了用戶臨時表 Insert Undo 段,修改該 Undo 段的狀態(tài)。
根據(jù) Insert Undo 段是否能直接被復(fù)用,Insert Undo 段的狀態(tài)會被修改為 TRX_UNDO_CACHED 或者 TRX_UNDO_TO_FREE。
如果 Insert Undo 段只管理了一個 Undo 頁,并且該 Undo 頁已使用空間小于四分之三,Undo 段的狀態(tài)被修改為 TRX_UNDO_CACHED,表示 Undo 段可以緩存起來直接復(fù)用。否則,Undo 段的狀態(tài)被修改為 TRX_UNDO_TO_FREE,表示 Undo 段以及它管理的 Undo 頁需要被釋放。
2. 生成事務(wù)提交號
如果事務(wù)分配了用戶普通表 Update Undo 段,或者用戶臨時表 Update Undo 段,事務(wù)提交過程中,需要生成事務(wù)提交號。事務(wù)提交完成之后,purge 線程會根據(jù)這個事務(wù)提交號,決定什么時候清理該 Update Undo 段管理的 Undo 頁中的 Undo 日志。
和事務(wù) ID 一樣,事務(wù)提交號也來源于事務(wù)子系統(tǒng)(trx_sys)的 next_trx_id_or_no 屬性。
事務(wù)啟動時,直接獲取 trx_sys->next_trx_id_or_no 屬性的值,作為事務(wù) ID,然后該屬性值加 1。事務(wù)提交時,直接獲取 trx_sys->next_trx_id_or_no 屬性的值,作為事務(wù)提交號,然后該屬性值加 1。這意味著事務(wù) ID 和事務(wù)提交號由同一個流水線生產(chǎn),同一個事務(wù)的提交號總是大于事務(wù) ID。
生成的事務(wù)提交號會保存到事務(wù)對象(trx)的 no 屬性中。
生成事務(wù)提交號之后,當(dāng)前正在提交的事務(wù)對象(trx)會加入事務(wù)子系統(tǒng)(trx_sys)的 serialisation_list 鏈表的末尾。這個鏈表中的所有事務(wù),都是正在提交的事務(wù)。更嚴(yán)格的來說,這些事務(wù)都是已經(jīng)生成了事務(wù)提交號,但是還沒有提交完成的事務(wù)。
3. 回滾段加入 purge 隊列
如果事務(wù)分配了 Update Undo 段,該 Undo 段所屬的回滾段需要加入到 purge 隊列中,表示該回滾段下有需要 purge 線程清理的 Undo 日志。
如前所述,回滾段會按需加入 purge 隊列:
- 如果用戶普通表回滾段下分配了 Update Undo 段,并且該回滾段目前不在 purge 隊列中,則加入 purge 隊列。
- 如果用戶臨時表回滾段下分配了 Update Undo 段,并且該回滾段目前不在 purge 隊列中,則加入 purge 隊列。
InnoDB 給同一個事務(wù)分配的的用戶普通表回滾段和用戶臨時表回滾段,如果都需要加入 purge 隊列,不能各自為戰(zhàn),而是打包加入。
這個包怎么打?
InnoDB 會創(chuàng)建一個 TrxUndoRsegs 對象,這個對象有個 m_rsegs 屬性,是個數(shù)組。
如果用戶普通表回滾段需要加入 purge 隊列,先加入到 m_rsegs 數(shù)組中。
如果用戶臨時表回滾段需要加入 purge 隊列,也加入到 m_rsegs 數(shù)組中。
然后,事務(wù)對象(trx)的 no 屬性中保存的事務(wù)提交號,也保存一份到 TrxUndoRsegs 對象的 m_trx_no 屬性中。
打完包之后,TrxUndoRsegs 對象會被加入 purge 隊列。
為了邏輯統(tǒng)一,如果事務(wù)只分配了用戶普通表回滾段、用戶臨時表回滾段兩者之一,回滾段也會打包成 TrxUndoRsegs 再加入 purge 隊列。
4. 處理 Update Undo 段
用戶普通表 Update Undo 段和用戶臨時表 Update Undo 段的處理邏輯一樣。下面以用戶普通表 Update Undo 段為例,介紹事務(wù)提交過程中 Update Undo 段需要進(jìn)行的操作。
4.1 Undo 段狀態(tài)
如果事務(wù)分配了用戶普通表 Update Undo 段,現(xiàn)在需要修改它的狀態(tài)了。和 Insert Undo 段一樣,滿足條件的 Update Undo 段也可以被緩存起來直接復(fù)用。
如果 Update Undo 段只管理了一個 Undo 頁,并且該 Undo 頁已使用空間小于四分之三,這個 Update Undo 段可以被緩存起來直接復(fù)用,它的狀態(tài)會被修改為 TRX_UNDO_CACHED。否則該 Undo 段不能被復(fù)用,它的狀態(tài)會被修改為 TRX_UNDO_TO_PURGE,表示等待 purge 線程清理 Update Undo 段管理的 Undo 頁中的 Undo 日志。
4.2 Undo 日志頁數(shù)量
對于狀態(tài)為 TRX_UNDO_TO_PURGE 的 Update Undo 段,回滾段首頁中保存著該 Undo 段首頁的頁號的小格子(Undo Slot)的值會被修改為 4294967295(代碼里為 FIL_NULL),也就解除了回滾段和該 Undo 段的關(guān)系。
Update Undo 段管理的 Undo 頁的數(shù)量,會累加到回滾段頭信息的 TRX_RSEG_HISTORY_SIZE 屬性中,這個屬性表示回滾段的 history 鏈表中所有 Undo 日志組占用的不會再寫入 Undo 日志的 Undo 頁的數(shù)量之和。
4.3 Undo 日志組
本小節(jié)介紹的內(nèi)容,狀態(tài)為 TRX_UNDO_CACHED 和 TRX_UNDO_TO_PURGE 的 Update Undo 段都需要操作。
當(dāng)前 Update Undo 段中,正在提交的事務(wù)產(chǎn)生的 Undo 日志所在的 Undo 日志組,會加入回滾段的 history 鏈表的頭部,等待 purge 線程清理其中的 Undo 日志。
給事務(wù)子系統(tǒng)(trx_sys)的 rseg_history_len 屬性值加 1,表示回滾段的 history 鏈表中等待 purge 線程清理 Undo 日志的 Undo 日志組又增加了一組。
代碼里實現(xiàn)的 rseg_history_len 加 1 的過程有一點點復(fù)雜,這里描述的是結(jié)果,也就是 Update Undo 段中一個 Undo 日志組加入了 history 鏈表,rseg_history_len 就會加 1。
rseg_history_len 加 1 之后,還會判斷相加的結(jié)果是否大于閾值。如果大于,意味著回滾段的 history 鏈表中等待清理的 Undo 日志組有點多。此時,如果 purge 線程處于休眠狀態(tài),會喚醒 purge 線程開始清理 Undo 日志。
接著還要把事務(wù)對象(trx)的 no 屬性中保存的事務(wù)提交號,寫入回滾段頭信息的 TRX_RSEG_MAX_TRX_NO 屬性中、Undo 日志組頭信息的 TRX_UNDO_TRX_NO 屬性中。
如果這個 Undo 日志組中既不包含 Delete 或者 Update 操作標(biāo)記刪除記錄產(chǎn)生的 Undo 日志,也不包含修改溢出字段產(chǎn)生的 Undo 日志,還會把 Undo 日志組頭信息的 TRX_UNDO_DEL_MARKS 屬性值修改為 false,purge 線程清理 Undo 日志過程中讀取到這組 Undo 日志時,就知道不需要執(zhí)行物理刪除表中記錄的操作。
如果管理當(dāng)前 Update Undo 段的回滾段不在 purge 隊列中,會加入 purge 隊列。否則,不需要重復(fù)加入。purge 線程清理完回滾段的 history 鏈表中一個 Undo 日志組的所有 Undo 日志之后,接下來就會清理下一組。
回滾段的 history 鏈表中,Undo 日志組按照自己頭信息的 TRX_UNDO_TRX_NO 屬性中保存的事務(wù)提交號,由小到大串連起來。事務(wù)提交號最小的 Undo 日志組在 history 鏈表末尾,事務(wù)提交號最大的 Undo 日志組在 history 鏈表頭部。
purge 線程清理時,先清理事務(wù)提交號小的 Undo 日志組中的 Undo 日志,再清理事務(wù)提交號大的 Undo 日志組中的 Undo 日志。
4.3 Undo 段鏈表
前面那些操作完成之后,就進(jìn)入收尾階段了。Update Undo 段會從回滾段的 update_undo_list 鏈表中移除。
如果 Update Undo 段的狀態(tài)為 TRX_UNDO_CACHED,還會加入回滾段的 update_undo_cached 鏈表頭部,等待復(fù)用。
如果 Update Undo 段的狀態(tài)為 TRX_UNDO_TO_PURGE,則釋放它的內(nèi)存對象。Undo 表空間中該 Undo 段及它管理的 Undo 頁都不會釋放,需要等到 purge 線程清理完 Undo 日志之后才能釋放。
5. 清理 Insert Undo 段
前面已經(jīng)確定了 Insert Undo 段的狀態(tài),現(xiàn)在是時候根據(jù)狀態(tài)處理 Insert Undo 段了。
首先,從回滾段的 insert_undo_list 鏈表中刪除 Insert Undo 段。
然后,如果 Insert Undo 段的狀態(tài)為 TRX_UNDO_CACHED,把它加入到回滾段的 insert_undo_cached 鏈表頭部。
如果 Insert Undo 段的狀態(tài)為 TRX_UNDO_TO_FREE,先釋放 Undo 表空間中該 Insert Undo 段及它管理的 Undo 頁,再釋放 Undo 段的內(nèi)存對象。
6. 總結(jié)
事務(wù)提交過程中,Undo 相關(guān)的流程如下:
- 修改 Insert Undo 段的狀態(tài)為 TRX_UNDO_CACHED 或者 TRX_UNDO_TO_FREE。
- 生成事務(wù)提交號。
- 把管理 Update Undo 段的回滾段加入到 purge 隊列中。
- 從回滾段的 update_undo_list 鏈表中移除 Update Undo 段??梢员痪彺娴?Update Undo 段,還需要加入 update_undo_cached 鏈表。
- 從回滾段 insert_undo_list 鏈表中移除 Insert Undo 段??梢员痪彺娴?Insert Undo 段,還需要加入 insert_undo_cached 鏈表。