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的, 所以很有必要詳細梳理這塊的知識, 總結記錄.
-
數據變化日志
當我們改變一個數據塊時, 都記錄了哪些日志, 具體是怎么樣的流程呢?
當在Oracle中改變一條數據時, 不僅僅要在數據文件里(可能在buffer里直接找到)找到並修改數據, 更重要的是需要做完善的日志記錄, 具體如下:- 創建一個重做改變向量, 描述如何往undo塊插入一條undo記錄(即undo的redo日志)
- 創建一個重做改變向量, 描述數據塊的變化(即data的redo日志)
- 合並這兩個重做改變向量為一條日志記錄, 並寫到重做日志緩沖區
- 向undo塊插入undo記錄
- 改變數據塊中的數據
圖1: 更新操作經歷的事件時序圖
-
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
-
- undo
-
讀一致性
塊的ITL entries里必須包含一個指向undo記錄的指針(指針是有限的)
-
回滾
-