了解mysql的undo log


第一次了解mysql的時候,看到了undo log這個名詞,卻不知道undo log是干什么,為了能夠繼續看明白一些mysql的資料,不得不先弄明白undo log是什么? undo log的原理是什么?它與數據庫的其它特性如何配何。
這篇筆記只從原理上分析,不涉及具體的實現方法。

undo log是什么?

undo log是一種日志,日志中記錄對於數據庫的反向操作。
如果把數據庫的內容當做一種狀態機,那么數據的寫操作就是修改狀態機的命令,而undo 就對應修改狀態機的反向命令。
所以理論上每一個對於狀態機修改的命令都會產生對應一條相當的undo log,以便事務回滾的時候,能夠把狀態機修改到事務原來的樣子。
假如我們有一個事務:
create table table1(c1 int);
begin transaction;
insert into table1 (100); //數據庫執行這一條命令的時候應該產生一條undo日志,能夠把這語句對於狀態機的修改回復到原來沒有修改的狀態 , undo 應該是 delete table1 where c1 = 100;
insert into table1(200); // undo 應該是 delete table1 where c1 = 200;
update table1 set c1 = 300 where c1 = 200; // undo: update table1 set c1 = 200 where c1 = 300
rollback;

為什么要有undo log, 或者說undo log解決了什么問題?

其實這個問題問的不好,因為undo是設計出來,沒有什么直接的因素說非得有undo log, 自己比較熟習的postgres就沒有undo log。下面就與postgres進行比較,來看mysql為什么有undo
mysql與postgres都是基於mvcc的,但是mysql與postgres對於mvcc的實現不太一樣。
為什么要有mvcc? 因為mvcc與它的前輩lock based相比,能夠實現讀寫不沖突這一個特性,如果沒有mvcc,一個事務讀一條數據,另外一個事務寫同一條數據,這兩個事務是無法並發執行的,后一個事務必須要等前一個事務執行完成之后才能執行,但是有了mvcc,這兩個事務就可以並發執行,這及大的提高的數據庫的性能,以至於現在主流的數據庫都實現了mvcc。
但是各個數據對於mvcc的實現略有不同:
其中mysql的實現就有undo log,而postgres的實現里面沒有undo log.但是postgres里面有clog, clog記錄了每一事務的狀態。
postgres的每一行的所有的版本是存放一起的,它允許保留aborted事務產生的版本, mysql的最新版本保存在表空間,而歷史版本則保存在undo log里面。mysql在事務回滾的時候,應該是同步利用undo log把最新版本的恢復成修改之前的狀態,同時刪除對應事務產生的undo log,而postgres則是通過異步的vaccum,來把aborted事務產生的歷史版本給刪除。
mvcc里面最重要的一點就是行可見性判斷。這里簡單的描述一下postgres的行可見性判斷與mysql的行可見性判斷。

  • mysql與postgres都會為每一個事務賦予一個xid,這個xid在數據庫內部是單高遞增的,它唯一標識了一個事務,同時它定義了數據庫中事務相關事件的先后順序。(這里的事務相關事件主要指begin transaction)
  • mvcc里面的snapshot讀要求每一個事務(或者第一個語句,因為事務隔離級別的不同決定是一個事務一個snapshot還是一個語句一個snapshot)都要有一個snapshot,而mysql與postgres都選擇使用當前活動事務的列表來作為snapshot. 每一個事務開始的時候(一個語句開始的時候)獲取一下當前整個系統所有已經開始但沒有結束的事務的xid.
  • mysql與postgres對於每一行的每一個版本都記錄了兩個字段x_min, x_max(mysql不知道叫啥,但是意思應該差不多), x_xmin代表創建這一個版本對應事務的xid, x_max表示刪除這個版本對應的事務的xid
  • 判斷一個版本是否可見,postgres需要以下幾個信息:
    • 當前事務的snapshot(活動事務列表)
    • clog,可以根據xid來查詢當前事務的狀態,事務有三個狀態pending, committed, aborted
    • 一個版本上的x_min, x_max
    • 首先,postgres先看一下,x_min對應的xid是否在snapshot中能夠找到,如果能夠找到,說明對應snapshot開始的時候,創建這個版本的事務還沒有結束,直接返回該版本不可見
    • 如果x_min在在snapshot里面找不到,說明在取snapshot的時候,創建這一行的事務已經結束,事務結束分兩種狀態,aborted, committed
      • 通過查詢clog,看x_min對應的事務是不是aborted, 如果是aborted, 說明創建這一行的事務已經回滾, 該行不可見
      • 如果事務是committed: 說明創建該行的動作在snapshot之前已經結束,這個時候還需要分情況:
        • 該行的x_max為空,就行是最新的版本,直接返回該行可見
        • 該行有x_max, 但是x_max對應的事務也在snapshot列表里面,說明取snapshot的時候,刪除該行的事務還沒有結束,該操作對應snapshot不可見,返回該行可見。
        • 該行有x_max, 同時x_max也不在snapshot列表里面,但是x_max對應的clog為aborted, 說明刪除操作對應的事務aborted,返回該行可見
        • 該行有x_max, 同時x_max也不在snapshot列表里面,但是x_max對應的clog為committed, 說明刪除該行的操作在snapshot之前已經成功,返回該行不可見。
  • mysql的行可見性應該會簡單一點,因為歷史版本里面只保存成功提交的版本
    • 行可見性判斷的幾個參數:對應事務(語句)的snapshot, x_min, x_max
    • x_min存在於snapshot中,該行不可見
    • x_min不存在於snapshot中,沒有x_max, 該行可見。(該行是最新版本ujn8)
    • x_min不存在於snapshot中,同時x_max也不存在於snapshot列表中,該行不可見,說明刪除操作在snapshot之前已經完成。

什么時候要使用到undo

上面已經提到,undo這個動作發生成rollback一個事務的時候。
同時也發生在數據recovery的時候,因為有些事務正在執行過程中,數據crash了,那么數據庫重啟做完redo后,要把對應沒有提交的事務的動作undo一下。
同時undo log記錄歷史版本,讀歷史版本的時候,也要從undo log里面去讀。

以上關於mysql的都是自己從互聯網上看資料得來的,理解的並不一定正確,后面會根據mysql代碼來驗證一下。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM