1. 程式人生 > 資料庫 >Mysql初探:三大日誌概述

Mysql初探:三大日誌概述

此文為極客時間:MySQL實戰45講的 2、15節日誌相關部分和網上一些相關文章的內容的總結

一、redo log

1.概述

redo log又叫重做日誌,提供的是資料丟失後的前滾操作。

redo log是innodb引擎獨有的日誌,使用了 WAL 技術(Write-Ahead Logging),也就是預寫日誌。它的關鍵點就是先寫日誌,再寫磁碟。對應到Mysql中具體操作,就是每次更新操作,先寫日誌,然後更新記憶體資料,最後等系統壓力小的時候再進行IO更新磁碟資料。避免了每一次更新都需要進行IO操作。redo log 是保證了事務永續性的關鍵

redo log 一般用在資料庫恢復的情況:

  1. 如果是正常執行的例項的話,資料頁被修改以後,跟磁碟的資料頁不一致,稱為髒頁。最終資料落盤,就是把記憶體中的資料頁寫盤。這個過程,甚至與 redo log 毫無關係
  2. 在崩潰恢復場景中,InnoDB 如果判斷到一個數據頁可能在崩潰恢復的時候丟失了更新,就會將它讀到記憶體,然後讓 redo log 更新記憶體內容。更新完成後,記憶體頁變成髒頁,就回到了第一種情況的狀態。

另外,redo log與undo log都被叫做事務日誌

2.組成

首先 redo log 包括兩部分:

  • 記憶體中的日誌緩衝(redo log buffer),該部分日誌是易失性,的其中又分為三部分:
    1. Buffer Pool
    2. Log Buffer
    3. 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 + 修改資料這樣的格式去記錄資料。

image-20200930111506778

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.概述

image-20201011182232866

當innodb執行修改時,會經歷一個兩階段提交的過程:

  • 執行器根據sql寫入新資料,然後新資料更新到記憶體裡
  • 將這個更新操作記錄到 redo log 裡面,此時 redo log 處於 prepare 狀態。然後告知執行器執行完成了,隨時可以提交事務。
  • 執行器生成這個操作的 binlog,並把 binlog 寫入磁碟
  • 執行器呼叫引擎的提交事務介面,引擎把剛剛寫入的 redo log 改成提交(commit)狀態,更新完成

那麼如果事務提交過程中出現了異常,資料庫崩潰了,就會有以下幾種情況:

  • 寫 binlog 前崩潰了:對於時刻A,redo log 還是 prepare,binlog 沒寫,此時崩潰後事務回滾。

  • 寫 binlog 後崩潰了:對於時刻B,redo log 還是 prepare,binlog 已經寫了,此時發生崩潰後情況如下:

    1. 如果redo log 已經標記為 commit,則提交事務,重做

    2. 如果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的二次提交機制,為事務的一致性提供了一定的保證