MySQL之日誌


MySQL日誌有3種:

redo

官方文檔:https://dev.mysql.com/doc/refman/8.0/en/innodb-redo-log.html

redo log是重做日誌,提供前滾操作。
redo log是物理日誌,記錄的是數據行的物理修改,當數據需要恢復時,可以從redo log中恢復到數據最後一次的修改狀態。
redo log包含兩塊內容,一部分是日誌緩衝(log buffer),在內存中;另一部分是日誌重做文件(log file),在磁盤上。

InnoDB通過force log at commit實現事務的持久性,在事務提交的時候,必須先將該事務的所有事務日誌寫入redo log file和undo log file中。

流程:
我們來看下日誌的寫入過程:
在這裏插入圖片描述
如上圖所示,我們來梳理下redo log的處理流程:

  1. 當數據發生了修改,InnoDB引擎先將該記錄寫入redo log buffer中,更新內存中的數據,此時更新就算是完成了。
  2. 在適當的時機,InnoDB引擎調用os方法,將buffer中的數據寫入磁盤。

另外redo log是固定大小的,是循環寫,當空間用完之後,後續記錄會將最老的記錄替換掉。

flush data
那MySQL是在什麼時候將buffer中的數據寫入log file呢?
通過配置:innodb_flush_log_at_trx_commit來決定,有0,1,2三種值,默認爲1:
在這裏插入圖片描述

  • 爲0的時候,事務提交時不會將log buffer中日誌寫入到os buffer,而是每秒寫入os buffer並調用fsync()寫入到log file on disk中。也就是說設置爲0時是(大約)每秒刷新寫入到磁盤中的,當系統崩潰,會丟失1秒鐘的數據。
  • 爲1的時候,事務每次提交都會將log buffer中的日誌寫入os buffer並調用fsync()刷到log file on disk中。這種方式即使系統崩潰也不會丟失任何數據,但是因爲每次提交都寫入磁盤,IO的性能較差。
  • 爲2的時候,每次提交都僅寫入到os buffer,然後是每秒調用fsync()將os buffer中的日誌寫入到log file on disk。

數據恢復:
在這裏插入圖片描述
log file以log block爲單位進行存儲,一個block大小爲512字節,
write point:數據已經刷新到磁盤上的位置點
check point:數據已經完整刷新到磁盤上的位置點

當啓動InnoDB時,無論上一次是正常關閉還是異常關閉,重啓InnoDB時,都會從check point開始到write point爲止進行恢復數據。

undo

官方文檔:https://dev.mysql.com/doc/refman/8.0/en/innodb-undo-logs.html

undo log是回滾日誌,提供回滾操作。
在InnoDB中,使用undo log來實現多版本併發控制。

在操作任何數據之前,首先先將數據備份到一個地方(這個數據備份的地方就是undo log),任何進行數據修改。如果出現錯誤或者用戶執行rollback,MySQL可以用 undo log的備份數據,將數據恢復到數據開始之前。

與redo log不同,redo log是物理日誌,undo log是邏輯日誌:

  • 當delete一條記錄,undo log中會新增一條對應的insert記錄
  • 當insert一條記錄,undo log中會新增一條對應的delete記錄
  • 當update,undo log中會新增一條相反的update記錄

當事務提交後,InnoDB不會立即刪除undo log中的記錄,會將該事務對應的undo log放入到刪除列表中,未來通過purge來刪除。

binlog

binlog是MySQL server的日誌文件,在server層,主要負責MySQL功能層面的事情。

與redo log的區別:

  1. redo,undo是InnoDB獨有的,binlog是所有引擎都可以使用的
  2. redo是物理日誌,記錄的是對某個數據頁做了哪些具體的修改,undo,binlog是邏輯日誌,記錄原始邏輯
  3. redo是循環寫,空間會用完,binlog是文件追加,不會覆蓋之前的信息

數據恢復:
找一個備份的時間點,把備份的binlog取出來,重放到要恢復的那個時刻

事務

我們來簡單回顧下數據庫事務的幾大特性:

  • Atomicity:原子性,通過undo日誌實現
  • Consistency:一致性,通過A+I+D三個特性組合保證
  • Isolation:隔離性,通過鎖
  • Durability:持久性,通過redo日誌實現

簡單梳理下數據庫事務的處理過程:
在這裏插入圖片描述
以update事務爲例,流程大體如下:

  1. 執行器先從引擎中找到數據,如果數據不在內存,則從磁盤中加載數據進入內存
  2. InnoDB數據引擎拿到數據之後,將數據在內存中進行更新,同時將數據記錄寫入redo,undo,此時數據處於prepare階段,InnoDB引擎通知執行器操作完成
  3. 執行器寫入本次操作的binlog
  4. 執行器調用引擎的事務提交接口,引擎把剛寫入的redo,undo狀態改成commit,同時將binlog寫入磁盤,事務完成

兩階段:
我們可以看到上述流程,先是經歷了prepare階段,再是commit階段,爲什麼要設計經歷兩個階段呢,直接寫入redo,binlog會有什麼問題呢?

現有一條記錄數據列:a=1,執行update指令:a=2,

  1. 如果一次性先寫入redo,再寫入binlog:
    假設redo log已經寫入完成,binlog還沒有寫入完成,此時進程異常,MySQL退出。後面再重啓數據庫,通過redo log是可以將數據恢復到a=2的,因爲redo log裏面是有記錄這個操作的。但是binlog裏面是沒有這個操作的記錄,後續通過備份日誌進行數據恢復,因binlog中這個記錄丟失,恢復後的數據是a=1,而實際應該是a=2,恢復後的數據是不準確的。
  2. 如果一次性先寫入binlog,再寫入redo:
    情形是類似的,binlog寫入完成,而redo沒有完成寫入,那麼InnoDB重啓之後,數據是無法恢復到a=2,最終的數據還是a=1,而用binlog恢復之後得到的數據確實a=2。

通過兩段式提交,確保redo,binlog裏面記錄的數據,最終是一致的。

PS:binlog默認是關閉的,需要手動開啓。在binlog關閉狀態下,事務是不需要兩階段提交的,參與者就只有redo和undo,中間需要通過互斥鎖來防止資源競爭。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章