面試官:MySQL Redo Log 和 Undo Log 有什么區(qū)別?分別用在什么場景?
大家好,我是君哥。國慶快樂。
MySQL 有三大日志體系,Redo Log、Undo Log 和 Binlog,其中 Undo Log 和 Redo Log 都是 InnoDB 存儲引擎特有的日志。今天就來聊一聊 MySQL Redo Log 和 Undo Log 的區(qū)別。
1.Undo Log
1.1 MVCC
MVCC 中文名稱叫多版本并發(fā)控制,是InnoDB 引擎為了提高執(zhí)行事務的并發(fā)效率引入的概念。多版本的含義就是一條數(shù)據(jù)被多個事務修改后,在事務還沒有被提交前,存在多個版本。MVCC 的實現(xiàn)機制就是通過版本鏈把多個版本連接起來。
比如我們有一個庫存表,記錄了主鍵 id,商品編號(goods_no)和庫存數(shù)量(stock_count)。商品編號等于 10 的記錄,初始狀態(tài) stock_count 是 100,事務一進行修改后 stock_count 變成 98,事務二進行修改后變成 95。
在事務一修改商品編號等于 10 的這條記錄之前,InnoDB 會先把 stock_count 等于 100 的原始記錄保存到 Undo Log,修改后的版本會保存指向原始記錄的 Undo Log 指針。同樣,事務二修改這條記錄之前,InnoDB 會將事務一修改后(stock_count 等于 98)的記錄保存到 Undo Log,事務二修改后的記錄保存指向這條 Undo Log 記錄的指針。這樣對同一條數(shù)據(jù)的修改形成了一個版本鏈,如下圖:
圖片
為了實現(xiàn)版本鏈,InnoDB 引擎在每行記錄中會添加 3 個隱藏的列:
- DB_TRX_ID:修改(插入、更新或刪除)這一條數(shù)據(jù)的事務 id;
- DB_ROLL_PTR:回滾指針,也就是上面說的指向前一個歷史版本的指針,用于回滾操作。
- DB_ROW_ID:當表中不定義主鍵時用作主鍵來自動生成聚簇索引。
1.2 Undo Log
在 InnoDB 中,事務為了保證原子性,必須要實現(xiàn)整個事務的回滾,這就需要記錄數(shù)據(jù)被修改前的狀態(tài)(比如插入或刪除了一條記錄,修改了一條記錄),記錄這些修改前狀態(tài)的日志就是 Undo Log。
Undo Log 主要有兩個作用:
- 原子性:事務可以通過 Undo Log 來實現(xiàn)回滾,從而實現(xiàn)原子性;
- 一致性:如果另一個事務需要將原始數(shù)據(jù)視為一致性讀取操作的一部分,則從 Undo Log 中檢索未修改的數(shù)據(jù)。
- MVCC:多版本并發(fā)控制中的多個版本,就是通過 Undo Log 來記錄的。
Undo Log 保存在 undo log segments, 而 undo log segments 則保存在 Rollback Segment。 Rollback segment 保存在 undo 表空間(undo tablespaces)和全局臨時表空間(global temporary tablespace)。
圖片
Rollback Segment 能支持的事務數(shù)量取決于自己能保存多少 undo slot 和每個事務需要多少個 undo slot。在一個頁大小是 16KB 的 InnoDB 引擎中,每個 Rollback Segment 能保存 1024 個 undo slot。
每個事務只能使用一個 Rollback Segment,一個 Rollback Segment 同一時刻可以會服務于多個事務。
那 Undo Log 什么時候清理呢?需要兩個條件:事務已經提交,并且 Undo Log 已經過期(即保存時間超過 undo retention 參數(shù)指定的時間)。
當然,即使達到清理的條件,也不會立刻清理,因為 Undo Log 所在的 Rollback Segment 頁可能被其他未提交的事務使用(數(shù)據(jù)頁使用未滿3/4)。
對于 insert 操作產生的 Undo Log,因為只對本事務可見,所以事務提交后 Undo Log 就可以立刻刪除。而對于 update 和 delete 操作,可能存在 MVCC 版本鏈的使用,不能在事務提交后立刻刪除。
Undo Log 具備刪除條件時,會放入隊列,等待后臺 purge 線程刪除。
2.Redo Log
Redo Log 也是 InnoDB 存儲引擎特有的日志,它是一個物理日志,記錄了在某個數(shù)據(jù)頁上做了哪些修改,主要用于崩潰恢復。
2.1 寫日志
在寫日志時,Redo Log 是循環(huán)寫,空間大小固定,寫滿后會覆蓋掉前面的日志。
Redo Log 采用固定大小的文件組,比如下圖文件組配置了 4 個文件,每個文件大小相同,寫滿一個后接著寫下一個,全部寫滿后就清除一部分前面的日志,繼續(xù)寫入。write pos 控制當前寫入的位置,check point 控制可以寫入的最后位置,如果兩個點重合了,那就需要清除部分日志,讓 check point 后移。
圖片
2.2 崩潰恢復
Redo Log 主要用于崩潰恢復。它確保了數(shù)據(jù)庫宕機后,已提交事務的數(shù)據(jù)不會丟失。Redo Log 基于 WAL (Write-Ahead Logging) 原則,即先寫日志,再寫磁盤。事務提交時,先將修改內容的記錄到 Redo Log,MySQL 宕機重啟后,利用 Redo Log 做崩潰恢復?;謴瓦^程如下圖:
圖片
首先,InnoDB 會檢查數(shù)據(jù)頁的 LSN (日志序列號),并與 Redo Log 中的 LSN 對比。Redo Log 上 LSN 比數(shù)據(jù)頁大的就是需要重做的數(shù)據(jù)。 接著,InnoDB 會掃描 Redo Log 中要恢復的日志,如果日志狀態(tài)是 COMMIT,則直接重做。如果日志狀態(tài)是 PREPARE,則還要去檢查對應的 Binlog,如果該事務的 Binlog 存在且完整,說明事務已經提交成功,應該重做。如果該事務的 Binlog 不存在或不完整,說明事務應該回滾,Redo Log 日志不進行重做。
通過這個機制,確保了 Redo Log 和 Binlog 的邏輯一致性:只要 Binlog 寫成功了,數(shù)據(jù)就一定能夠被恢復;如果 Binlog 沒寫成功,說明事務應該被回滾,數(shù)據(jù)無需恢復。
3.區(qū)別
總結 Undo Log 和 Redo Log 區(qū)別如下:
區(qū)別點 | Undo Log | Redo Log |
作用 | 事務回滾和MVCC | 奔潰恢復 |
日志類型 | 邏輯日志(記錄修改前的數(shù)據(jù)狀態(tài)) | 物理日志(記錄做了哪些修改) |
生命周期 | 在事務中數(shù)據(jù)修改前寫入,事務提交并且具備刪除條件后被清理。 | 事務進行中寫入 ,事務提交后可以被覆蓋。 |
清理方式 | purge 線程清理 | 循環(huán)寫入,被覆蓋。 |
存儲位置 | undo 表空間和全局臨時表空間 | 磁盤 ib_logfile 文件 |
最后,我們再以下面的 SQL 為例,看一下記錄日志的過程:
update tb_stock set stock_count = 98 where goods_no = 10;
圖片
































