MySQ回滾日志Undo Log實踐記錄
Undo Log 是 InnoDB 存儲引擎中實現(xiàn)事務的關鍵組件之一。它與 Redo Log 共同協(xié)作,確保了事務的原子性(Atomicity)和一致性(Consistency),同時也是 MySQL 實現(xiàn)多版本并發(fā)控制(MVCC) 的基礎。
一、什么是 Undo Log?
Undo Log,顧名思義,是一種用于撤銷操作的日志。它記錄了事務發(fā)生之前的數(shù)據(jù)狀態(tài)(主要是修改前的舊版本數(shù)據(jù))。
當執(zhí)行一個 DELETE、UPDATE 或 INSERT 操作時,InnoDB 不僅會生成 Redo Log 用于重做,還會生成相應的 Undo Log。如果事務需要回滾(ROLLBACK)或者系統(tǒng)崩潰后進行恢復,InnoDB 就可以利用 Undo Log 中的信息,將數(shù)據(jù)還原到修改前的狀態(tài)。
核心思想: 在修改任何數(shù)據(jù)之前,先“留底”,把舊數(shù)據(jù)拷貝一份到 Undo Log 中。
二、Undo Log 的主要作用
- 實現(xiàn)事務回滾(原子性)
- 這是 Undo Log 最直接的作用。當一個事務執(zhí)行失敗或用戶顯式執(zhí)行
ROLLBACK時,InnoDB 會讀取對應事務的 Undo Log,執(zhí)行相反的逆操作來撤銷更改:- 對于
INSERT,逆操作是DELETE。 - 對于
DELETE,逆操作是INSERT。 - 對于
UPDATE,逆操作是用舊值再UPDATE回去。
- 對于
- 實現(xiàn)多版本并發(fā)控制(MVCC)- 一致性讀
- 這是 Undo Log 在現(xiàn)代數(shù)據(jù)庫中最重要、最高頻的作用。MVCC 使得讀操作(SELECT)不會阻塞寫操作(UPDATE/DELETE),寫操作也不會阻塞讀操作。
- 當某個事務需要讀取一行數(shù)據(jù)時,InnoDB 會找到該行數(shù)據(jù)的一個“可見”版本。
- 如果該行數(shù)據(jù)的最新版本(由某個活躍事務修改)對當前讀事務不可見,InnoDB 就會沿著該行記錄的 DB_ROLL_PTR(回滾指針),在 Undo Log 中尋找更早的、符合當前事務隔離級別要求的舊版本數(shù)據(jù)。
- 這些舊版本數(shù)據(jù)鏈(版本鏈)就存儲在 Undo Log 中。因此,一個讀請求可能會訪問到很久之前的數(shù)據(jù)快照,這些快照就是通過 Undo Log 構建出來的。
三、Undo Log 的存儲與結構
1. 物理存儲
- 存儲位置: Undo Log 存儲在表空間中。從 MySQL 5.6 開始,可以配置為使用獨立的 Undo 表空間 (
.ibu文件),與系統(tǒng)表空間 (ibdata1) 分離,方便管理和擴展。 - 回滾段 (Rollback Segments): InnoDB 有 128 個回滾段(Rollback Segments),其中:
- 第 0 號、第 1 號、第 33-127 號回滾段存在于臨時表空間。
- 第 1-32 號回滾段存在于普通表空間(系統(tǒng)表空間或獨立 Undo 表空間)。
- 每個回滾段管理著多個 Undo Slot,每個 Slot 對應一個 Undo Log Segment。
- Purge 機制: Undo Log 不會永遠保留。當沒有任何事務或快照讀需要用到某個舊版本數(shù)據(jù)時(即該 Undo Log 不再被 MVCC 所需),這個 Undo Log 所占用的空間就可以被回收重用。這個刪除過期 Undo Log 的過程由后臺的 Purge 線程負責。
2. 邏輯結構 - 版本鏈
每一行記錄(聚簇索引)在 InnoDB 中都包含兩個隱藏字段:
DB_TRX_ID(6字節(jié)): 最近一次修改該行數(shù)據(jù)的事務 ID。DB_ROLL_PTR(7字節(jié)): 回滾指針,指向該行數(shù)據(jù)的上一個舊版本在 Undo Log 中的位置。
UPDATE 操作會形成一個版本鏈:
- 事務 A (Trx-id=100) 修改了某行數(shù)據(jù)。
- 修改前,該行的舊數(shù)據(jù)(包括
DB_TRX_ID和所有字段值)被拷貝到 Undo Log 中。 - 修改后,新行的
DB_TRX_ID被設置為 100,DB_ROLL_PTR指向剛剛創(chuàng)建的 Undo Log 記錄。 - 當事務 B (Trx-id=200) 再次修改這行數(shù)據(jù)時,過程重復:拷貝當前狀態(tài)到新的 Undo Log 記錄,然后更新數(shù)據(jù)行,并將新的
DB_ROLL_PTR指向事務 B 創(chuàng)建的 Undo Log 記錄。
這樣,通過 DB_ROLL_PTR,所有歷史版本的數(shù)據(jù)就像一條鏈表一樣被串聯(lián)起來,這就是版本鏈。
示例:
假設一行數(shù)據(jù)初始值為 Name=‘Alice’。
- 事務 100 將其改為
Name=‘Bob’。 - 事務 200 又將其改為
Name=‘Charlie’。
這行記錄及其版本鏈的結構如下:
當前行 (In Table) : [Name='Charlie', DB_TRX_ID=200, DB_ROLL_PTR --> Undo Record 200]
^
|
Undo Record 200 : [Name='Bob', DB_TRX_ID=100, DB_ROLL_PTR --> Undo Record 100]
^
|
Undo Record 100 : [Name='Alice', DB_TRX_ID=?, DB_ROLL_PTR -> NULL]當有一個 Read View 需要查詢這行數(shù)據(jù)時,它會從最新的記錄開始,順著 DB_ROLL_PTR 依次判斷哪個版本對它可見。
四、Undo Log 與 Redo Log 的區(qū)別
這是一個非常重要的概念,兩者的區(qū)別和聯(lián)系如下表所示:
| 特性 | Redo Log | Undo Log |
|---|---|---|
| 目的 | 重做日志,確保事務的持久性 | 回滾日志,確保事務的原子性和一致性讀(MVCC) |
| 內(nèi)容 | 記錄的是數(shù)據(jù)頁的物理變化(在某個頁上做了什么修改) | 記錄的是數(shù)據(jù)修改前的邏輯狀態(tài)(行的舊值) |
| 生成時機 | 在事務執(zhí)行過程中不斷寫入 | 在數(shù)據(jù)修改前生成 |
| 作用時機 | 數(shù)據(jù)庫崩潰恢復時,重放已提交的事務 | 事務回滾時和一致性讀(MVCC) 時 |
| 生命周期 | 事務提交后,對應的 Redo Log 可能很快被覆蓋(循環(huán)寫) | 事務提交后,Undo Log 可能仍被 MVCC 使用,不能立即刪除 |
| 磁盤存儲 | 順序?qū)懭耄?code>ib_logfile0/1) | 隨機寫入(存在于表空間) |
| 日志類型 | 物理邏輯日志(物理到頁,邏輯到行) | 邏輯日志 |
關鍵聯(lián)系: Undo Log 本身的操作(寫入、修改)也會產(chǎn)生 Redo Log。因為 Undo Log 也需要持久化,防止在寫入 Undo Log 過程中發(fā)生崩潰導致數(shù)據(jù)不一致。這被稱為 “Redo Log for Undo Log”。
五、相關參數(shù)與最佳實踐
innodb_undo_tablespaces: 設置獨立 Undo 表空間的個數(shù)。通常建議設置為 2 或更多,便于管理和空間回收。innodb_max_undo_log_size: 指定每個 Undo 表空間文件的最大大小(默認為 1GB)。超過此值,表空間會被標記為可截斷。innodb_undo_log_truncate: 是否啟用自動截斷(收縮)Undo 表空間的功能。強烈建議開啟(=ON),否則 Undo 表空間會無限增長。innodb_purge_threads: Purge 線程的數(shù)量。在高并發(fā)寫場景下,可以適當增加此值(如設置為 4)以加快過期 Undo Log 的清理速度。
最佳實踐:
- 啟用獨立 Undo 表空間和自動截斷,避免
ibdata1文件無限膨脹。 - 對于有大事務或長事務的系統(tǒng),需要特別關注 Undo Log 的增長。因為一個長時間未提交的事務會阻止 Purge 線程清理它之后產(chǎn)生的所有 Undo Log,可能導致 Undo 表空間急劇增長。
- 監(jiān)控
SHOW ENGINE INNODB STATUS\G輸出中的TRANSACTIONS部分,關注歷史鏈表長度(History list length),它代表了未 Purge 的 Undo Log 頁的數(shù)量。
總結
Undo Log 是 InnoDB 引擎的基石之一,它遠不止是“回滾”那么簡單。它的核心價值在于:
- 保障原子性:為事務回滾提供基礎。
- 實現(xiàn) MVCC:構建數(shù)據(jù)行的多版本,是實現(xiàn)非鎖定讀(快照讀)、提升數(shù)據(jù)庫并發(fā)性能的關鍵。
理解 Undo Log 的工作原理,對于深入掌握 MySQL 的事務機制、MVCC 以及進行性能調(diào)優(yōu)和故障排查都至關重要。
到此這篇關于MySQ回滾日志Undo Log實踐記錄的文章就介紹到這了,更多相關MySQ回滾Undo Log內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
mysql如何分別按年/月/日/周分組統(tǒng)計數(shù)據(jù)詳解
我們在用Mysql抽取數(shù)據(jù)時候,經(jīng)常需要按照天、周、月等不同的粒度對數(shù)據(jù)進行分組統(tǒng)計,下面這篇文章主要給大家介紹了關于mysql如何分別按年/月/日/周分組統(tǒng)計數(shù)據(jù)的相關資料,需要的朋友可以參考下2022-12-12
MySQL數(shù)據(jù)庫開發(fā)的36條原則(小結)
這篇文章主要介紹了MySQL數(shù)據(jù)庫開發(fā)的36條原則(小結),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-09-09
mysql創(chuàng)建表設置表主鍵id從1開始自增的解決方案
在MySQL中用很多類型的自增ID,每個自增ID都設置了初始值,一般情況下初始值都是從0開始,然后按照一定的步長增加(一般是自增 1),下面這篇文章主要給大家介紹了關于mysql創(chuàng)建表設置表主鍵id從1開始自增的解決方案,需要的朋友可以參考下2023-04-04

