Mysql初探:三大日誌概述
此文為極客時間:MySQL實戰45講的 2、15節日誌相關部分和網上一些相關文章的內容的總結
一、redo log
1.概述
redo log又叫重做日誌,提供的是資料丟失後的前滾操作。
redo log是innodb引擎獨有的日誌,使用了 WAL 技術(Write-Ahead Logging),也就是預寫日誌。它的關鍵點就是先寫日誌,再寫磁碟。對應到Mysql中具體操作,就是每次更新操作,先寫日誌,然後更新記憶體資料,最後等系統壓力小的時候再進行IO更新磁碟資料。避免了每一次更新都需要進行IO操作。redo log 是保證了事務永續性的關鍵。
redo log 一般用在資料庫恢復的情況:
- 如果是正常執行的例項的話,資料頁被修改以後,跟磁碟的資料頁不一致,稱為髒頁。最終資料落盤,就是把記憶體中的資料頁寫盤。這個過程,甚至與 redo log 毫無關係。
- 在崩潰恢復場景中,InnoDB 如果判斷到一個數據頁可能在崩潰恢復的時候丟失了更新,就會將它讀到記憶體,然後讓 redo log 更新記憶體內容。更新完成後,記憶體頁變成髒頁,就回到了第一種情況的狀態。
另外,redo log與undo log都被叫做事務日誌。
2.組成
首先 redo log 包括兩部分:
- 記憶體中的日誌緩衝(redo log buffer),該部分日誌是易失性,的其中又分為三部分:
- Buffer Pool
- Log Buffer
- OS Buffer
- 二是磁碟上的重做日誌檔案(redo log file),該部分日誌是持久的;
由於有時候一次事務可能有多次更新,比如:
begin;
insert into t1 ...
insert into t2 ...
commit;
這個事務要往兩個表中插入記錄,插入資料的過程中,生成的日誌都得先儲存起來,但又不能在還沒 commit 的時候就直接寫到 redo log 檔案裡。所以就有了 log buffer 這麼一塊記憶體,用來先存 redo 日誌的。記錄了一部分後,根據一定的條件,再最後寫入磁碟上的日誌檔案 log file。
3.日誌刷盤策略
由於log buffer處於使用者工作空間的記憶體中,要寫入磁碟上的日誌檔案log file還需要經過作業系統核心空間的os buffer。因此需要呼叫作業系統的fsync()來完成這一過程。
我們可以簡單的理解為os buffer到log files這一過程——也就是日誌從記憶體刷入磁碟的過程——為刷盤。每次事務的提交必然會產生log buffer,但是刷入磁碟的動作卻不一定與事務提交同步進行。
InnoDB 有個關鍵的引數 innodb_flush_log_at_trx_commit 控制 Redo Log 的刷盤策略,該引數有三個取值:
- 0:每秒刷一次盤。當設定為0的時候,事務提交時不會將 log buffer 中日誌寫入到 os buffer ,而是每一秒後才一次性將日誌寫入 os buffer 並呼叫 fsync() 寫入到 log file 中。當系統崩潰,可能會丟失1秒鐘的資料。
- 1:每次提交都刷盤。事務每次提交都會將 log buffer 中的日誌寫入 os buffer 並呼叫 fsync() 刷到 log file 中。這種方式即使系統崩潰也不會丟失任何資料,但是因為每次提交都寫入磁碟,IO的效能較差。
- 2:根據
innodb_flush_log_at_timeout
決定什麼時候刷盤。當設定為2的時候,每次提交都僅寫入到os buffer,然後是每秒呼叫fsync()將os buffer中的日誌寫入到 log file。
4.寫入策略
redo log是一個物理日誌,我們知道資料庫引擎載入是按“頁”來的,redo log記錄的就是每個“頁”上的資料發生的變化。但是不像 binlog 那樣,redo log 不記錄 sql,而是以類似 session_id + date + file_id + block_id + 修改資料這樣的格式去記錄資料。
redo log的日誌檔案大小是根據配置固定的,如果有一組有四個檔案,每個檔案的大小是 1GB,那麼總共就只能記錄4GB的日誌。
因為redo log是前滾日誌,也就是說一旦事務成功提交且資料持久化落盤之後,此時日誌中的對應事務資料記錄就失去了意義。所以redo log類似一個環形連結串列,從前往後寫,到底了就刪除最前面的再回到開頭往後寫。
有了 redo log,InnoDB 就可以保證即使資料庫發生異常重啟,之前提交的並且在日誌中有記錄都資料可以找回,這個能力稱為crash-safe。
二、undo log
undo log又叫回滾日誌。事務未提交之前,undo log儲存了未提交之前的版本資料,可作為資料舊版本快照供其他併發事務進行快照讀。
因此,他能夠提供兩個功能:
-
回滾:當執行rollback時,就可以從undo log中的邏輯記錄讀取到相應的內容並進行回滾。
簡單的說:如果我們執行了insert操作,那麼日誌中就會新增一條相反的delete的sql;
-
多行版本控制(MVCC):當讀取的某一行被其他事務鎖定時,它可以從undo log中分析出該行記錄以前的資料是什麼,從而提供該行版本資訊,讓使用者實現非鎖定一致性讀取。
undo log保證了事務的原子性。
三、binlog
binlog 又叫二進位制日誌。是 Server 層特有的日誌,無論哪個引擎都能使用。
它被用於記錄 mysql 的資料更新(即使更新零條或者刪除零條也會記錄)。
binlog有三種工作模式:
- Row :日誌中會記錄每一行資料被修改的情況,然後在slave端對相同的資料進行修改。
- Statement:每一條被修改資料的sql都會記錄到 master 的 binlog 中,slave 在複製的時候sql程序會解析成和原來 master 端執行過的相同的sql再次執行。
- Mixed:結合了 Row 和 Statement 的優點,同時 binlog 結構也更復雜。
四、redo log 和 binlog
- binlog 是 mysql 自帶的,redo log 是 innodb 引擎自帶的
- binlog 記錄的是每一行資料的變化或修改資料的 sql,redo log 記錄的是資料頁的變化
- binlog 能夠實現歸檔功能,通過 binlog 可以實現備份,redo log 是迴圈寫的,歷史日誌不會一直保留
- mysql 高可用基於 binlog,像主從等系統機制都依賴於 binlog
五、兩階段提交
1.概述
當innodb執行修改時,會經歷一個兩階段提交的過程:
- 執行器根據sql寫入新資料,然後新資料更新到記憶體裡
- 將這個更新操作記錄到 redo log 裡面,此時 redo log 處於 prepare 狀態。然後告知執行器執行完成了,隨時可以提交事務。
- 執行器生成這個操作的 binlog,並把 binlog 寫入磁碟。
- 執行器呼叫引擎的提交事務介面,引擎把剛剛寫入的 redo log 改成提交(commit)狀態,更新完成
那麼如果事務提交過程中出現了異常,資料庫崩潰了,就會有以下幾種情況:
-
寫 binlog 前崩潰了:對於時刻A,redo log 還是 prepare,binlog 沒寫,此時崩潰後事務回滾。
-
寫 binlog 後崩潰了:對於時刻B,redo log 還是 prepare,binlog 已經寫了,此時發生崩潰後情況如下:
-
如果redo log 已經標記為 commit,則提交事務,重做
-
如果redo log 還是 prepare,則去檢查 binlog 記錄的對應事務是否存在:
如果存在,就提交事務,重做
如果不存在,就回滾
-
2.為什麼需要兩階段提交
我們舉個反例,說明一下他的必要性。假設我們要更新某條資料的A欄位由0變為2:
- 先 redo log 再 binlog,服務掛了:由於 redo log 還在,可以通過 redo log 恢復資料,A此時是2。但是如果後面要通過binlog恢復資料時,由於binlog中沒有這次修改的記錄,恢復後的資料庫/備份庫就會變為0,丟失了這次更新。
- 先 binlog 再 redo log,服務掛了:由於 redo log 沒記錄這次更新,所以恢復後這次事務無效,A此時是0。但是 binlog 已經有了“A從0變成2這個記錄”,所以恢復以後等於多了一次事務。
之所以這樣做,歸根結底是為了保證資料庫事務的一致性:
因為不管是從庫或者備份庫都需要通過讀取 binlog 來同步資料,所以為了保證保證和主庫資料一致,binlog 裡記錄的每一條事務就必須是已經提交了的,也就是一定要保證往 binlog 裡寫入資料以後事務不能回滾。
總結
- redo log保證更新不丟失,支援的是事務的永續性
- undo log保證事務不成功可以回滾,支援的是事務的原子性
- redo log和binlog的二次提交機制,為事務的一致性提供了一定的保證