redo log
redo log叫做重做日志.用於解決數據庫事物提交 還未刷入磁盤,服務器down機導致的數據丟失的問題。
InnoDB作為MySQL的存儲引擎,數據存儲在磁盤中,如果每次讀寫數據都要操作磁盤IO效率會很低,為此InnoDB提供了緩存(Buffer Pool),Buffer Pool中包含了磁盤中部分數據頁的映射,作為訪問數據庫的緩沖:當從數據庫讀取數據時,會首先從Buffer Pool中讀取,如果Buffer Pool中沒有,則從磁盤讀取后放入Buffer Pool;當向數據庫寫入數據時,會首先寫入Buffer Pool,Buffer Pool中修改的數據會定期刷新到磁盤中(刷臟)
Buffer Pool的使用大大提高了讀寫數據的效率,但是也帶了新的問題:如果MySQL宕機,而此時Buffer Pool中修改的數據還沒有刷新到磁盤,就會導致數據的丟失,事務的持久性無法保證。
於是,redo log被引入來解決這個問題:當數據修改時,除了修改Buffer Pool中的數據,還會在redo log記錄這次操作;當事務提交時,會調用fsync接口對redo log進行刷盤。如果MySQL宕機,重啟時可以讀取redo log中的數據,對數據庫進行恢復。redo log采用的是WAL(Write-ahead logging,預寫式日志),所有修改先寫入日志,再更新到Buffer Pool,保證了數據不會因MySQL宕機而丟失,從而滿足了持久性要求。
既然redo log也需要在事務提交時將日志寫入磁盤,為什么它比直接將Buffer Pool中修改的數據寫入磁盤(即刷臟)要快呢?主要有以下兩方面的原因:
(1)刷臟是隨機IO,因為每次修改的數據位置隨機,但寫redo log是追加操作,屬於順序IO。
(2)刷臟是以數據頁(Page)為單位的,MySQL默認頁大小是16KB,一個Page上一個小修改都要整頁寫入;而redo log中只包含真正需要寫入的部分,無效IO大大減少。
undo log
undo log的2個主要作用是實現MVCC和事物回滾
當 delete 一條記錄時,undo log 中會記錄一條對應的 insert 記錄,反之亦然,當 update 一條記錄時,它記錄一條對應相反的 update 記錄,如果 update 的是主鍵,則是對先刪除后插入的兩個事件的反向邏輯操作的記錄。
在事物回滾時就會反向讀取相應內容進行回滾,也可以根據undo log讀取到被修改后數據的原值
MVCC的實現原理
MVCC的實現主要利用到了數據庫隱式字段和undo log、ReadView
MVCC的好處主要實現思想是通過數據多版本來做到讀寫分離。從而實現不加鎖讀進而做到讀寫並行。
MVCC結構
Column1 Column2位數據格式
DB_TRX_ID(隱式字段) 最近一次修改的事物id(注:事物id都是遞增的)
DB_ROLL_PTR(隱式字段) 回滾日志 指向undo log
DB_ROW_ID(隱式字段) 自增ID如果數據庫表沒有指定主鍵 則默認以此作為聚簇索引
undo log 版本鏈
事物A id=1的事物將小明改為小張
事物B id=2的事物將age改為30
ReadView
read view(快照)是基於undo log實現的可以實現不加鎖的情況下並發讀 讀已提交和可重復讀都是通過ReadView實現
快照讀和當前讀
詳情可可以看《快照讀和當前讀》
數據結構
m_ids:當前有哪些事務正在執行,且還沒有提交,這些事務的 id 就會存在這里
min_trx_id:是指 m_ids 里最小的值
max_trx_id:下一個要生成的事物id,因為事物id是遞增的 肯定比當前所有事物id要大
creator_trx_id: 創建read view的事物id
RE隔離級別讀已提交實現原理
圖1.事物A id等於1將數據修改並提交
圖2.事物B執行查詢
1.事物B執行快照讀(注:RR隔離級別只會生成一次快照 RE隔離級別每次都會生成新的快照)
2.讀取到數據通過數據隱藏列DB_TRX_ID值1跟min_trx_id做比較, DB_TRX_ID<min_trx_id 說明最近修改此數據的事物已經提交則可以正常讀取 column1=小張 column2=32
圖3
1.事物C將colunm2=32 改為16事物並未提交
2.事物B執行select讀取數據通過DB_TRX_ID與min_trx_id做比較 發現比最小事物id大。表示有可能讀到的。再將DB_TRX_ID=4在min_ids進行查找 成功找到。發現事物還未提交
3.通過undolog鏈跟往上找(通步驟2一樣的查找方式)成功查到 colunm1=小明 colunm2=32的數據 成功讀取
圖4
1.又回到了圖一。事物C提交事物后。事物B進行查詢重新生成快照(RE隔離級別每次都會生成新的快照)
2.這個時候m_ids里面已經沒有4了。數據行DB_TRX_ID>min_trx_id 同時又不在m_ids里面存在表示已經提交了直接讀取
RR可重復讀實現原理
流程都一樣 唯一不同的是只會生成一次快照,就算事物C提交了 m_ids[2,4] 還是有2和4 所以讀到的數據還是小明 32