數據庫中的undo日志、redo日志


MySQL中有六種日志文件,
分別是:重做日志(redo log)、回滾日志(undo log)、二進制日志(binlog)、錯誤日志(errorlog)、慢查詢日志(slow query log)、一般查詢日志(general log),中繼日志(relay log)。
其中重做日志和回滾日志與事務操作息息相關,二進制日志也與事務操作有一定的關系,這三種日志,對理解MySQL中的事務操作有着重要的意義。

 

undo是將用戶上一步做的操作對程序造成的改動恢復到改動之前,而redo操作是指重新實現這種改動。

undo/redo操作的實現方式分為兩類:記錄數據和記錄操作。

記錄數據是指將信息編輯窗口打開時,保存原始數據,然后記錄用戶每次操作后的結果數據,這里的數據是指信息編輯窗口中所有可能發生變動的數據。做undo操作時程序將用戶上一步操作前的數據傳給信息編輯窗口相應控件。這種做法是以空間來換時間,程序不必考慮用戶到底改變了哪些數據,反正每次都是替換的所有可能改變的數據。當每次保存的數據量比較小時,這種做法比較方便快捷,但是如果數據量大,比如包括圖形、視頻信息等,這種方法就比較耗費內存了。

 

記錄操作是指信息編輯窗口打開后,記錄用戶每次的操作,包括具體的操作動作以及操作改變的數據,這里的數據是指既能還原操作的數據又能重復操作的數據。做undo操作時程序根據記錄的用戶操作進行反向處理,對信息編輯窗口進行改動,而做redo操作的時候程序根據記錄的用戶操作來重復用戶的操作。這種做法是以時間換來空間,程序記錄的信息變少了,每次只需要記錄用戶的操作類型以及相關的操作數據(比如用戶編輯的哪個控件,編輯前后的控件內容分別是什么),與操作無關的其他數據則不需要記錄。這種做法比起記錄數據的方式肯定要復雜,但是勝於節儉內存。

 

Oracle中的redo和undo是關鍵技術的核心, 諸如實例恢復, 介質恢復, DataGuard, 閃回機制等都是給予redo和undo的, 所以很有必要詳細梳理這塊的知識, 總結記錄.

  1. 數據變化日志

    當我們改變一個數據塊時, 都記錄了哪些日志, 具體是怎么樣的流程呢?

    當在Oracle中改變一條數據時, 不僅僅要在數據文件里(可能在buffer里直接找到)找到並修改數據, 更重要的是需要做完善的日志記錄, 具體如下:
    • 創建一個重做改變向量, 描述如何往undo塊插入一條undo記錄(即undo的redo日志)
    • 創建一個重做改變向量, 描述數據塊的變化(即data的redo日志)
    • 合並這兩個重做改變向量為一條日志記錄, 並寫到重做日志緩沖區
    • 向undo塊插入undo記錄
    • 改變數據塊中的數據

    圖1: 更新操作經歷的事件時序圖

  2. Redo

    redo處理方式:

    session -> redo日志 -> 寫入redo log buffer -> 寫到redo日志文件(循環利用) -> 歸檔到日志中

    寫redo log buffer會成為系統的瓶頸

    session每次數據更改都會插入一條redo記錄到buffer中, 一個session可能很短時間內做了很多更改, 同時可能有很多session並發操作, 卻只有一個redo log buffer.

    • 老機制, redo allocation latch: 保護redo log buffer, 控制對共享區內存的訪問.

      session -> 請求redo allocation latch -> 為寫入到buffer的信息預留一些空間

      避免了多個進程同時寫入buffer相同部分的風險.

        大並發系統會出現latch競爭, cpu空轉
    • 新機制, private redo和IMU(In-Memory undo)

      在session的整個事務期間內, 生成所有改變向量, 寫入private redo log buffer(PGA中), 當事務提交時, session會將private redo buffer中的記錄copy到公共redo log buffer中.

      一個session在一次事務里只需要獲取一次公共的redo allocation latch.


      e.g

      腳步更新表記錄, 觀察期間latch統計信息.

      • 9i的輸出:

        Latch Gets Im_Gets
        redo copy 0 51
        redo allocation 53 0
        Name Value
        redo entries 51
        redo size 12,668
      • 10g的輸出:

        Latch Gets Im_Gets
        redo copy 0 1
        redo allocation 5 1
        In memory undo latch 53 1
        Name Value
        redo entries 1
        redo size 12,048

      10g里, redo copy latch只命中了一次; redo allocation latch也get很少; 只生成了一個redo entry;

      測試中發現, 貌似競爭問題轉移了?


    • 待跟進:
      • 分析v$latch_children, 搞清楚為什么latch活動的變化不是新的威脅(瓶頸);
      • 分析重做日志, 搞清楚那個大的日志條目(redo entry)都記錄了什么;
      • 分析動態性能表(x$kcrfstrand和x$ktifp), 理解各種實例活動信息是如何串聯到一起的;
    • redo的2組內存結構:
      • x$kcrfstrand, 私有redo區: 處理"前滾"改變向量(私有redo區里也包含傳統的"公共"redo log buffer);
      • x$ktifp, IMU區: 處理undo改變向量;

      IMU區中有N多IMU池, N取決於數據庫參數transactions/10, 每個池都有自己的latch.

      x$ktifp中的每條記錄(即IMU)在x$kcrfstrand中都有對應的一條private redo記錄.

      x$kcrfstrand中的每條private redo記錄都由其自身的redo allocation latch保護, 每條"公共"的重做記錄都由傳統的redo copy latch保護.

    • In-Memory undo latch(IMU latch):

      任何一個改變都會產生一次對IMU latch的訪問, 用一個latch(IMU latch)代替兩個(redo allocation latch與redo copy latch), 至少latch競爭是減半了.

      IMU latch有許多子latch, 每個子latch負責一個IMU內存區域(池).

    • 新機制的redo allocation latch:

      2種latch:
      • 一類保護私有redo線程(private redo thread)
      • 另一類保護公共redo線程(public redo thread)

      每個線程都有自己的latch

  3. undo
    • 讀一致性

      塊的ITL entries里必須包含一個指向undo記錄的指針(指針是有限的)

    • 回滾


免責聲明!

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



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