1. 程式人生 > 其它 >資料庫原理4---MySQL日誌

資料庫原理4---MySQL日誌

重做日誌redo log

redo log是重做日誌,為InnoDB儲存引擎獨有。它記錄了資料頁上的改動。當事務中修改了資料,將會備份儲存。
當發生資料庫伺服器宕機或者髒頁未寫入磁碟,可以通過redo log恢復。

redo log用於配合MySQL的WAL機制。MySQL進行更新操作時,為了能夠快速響應,所以採用了非同步寫回磁碟的技術,寫入記憶體後就返回。
但是這樣,會存在crash後記憶體資料丟失的隱患,而redo log具備crash safe的能力。

WAL,Write-Ahead Logging,關鍵點就是日誌先寫記憶體,再寫磁碟。
MySQL執行更新操作後,在真正把資料寫入到磁碟前,先記錄日誌。
好處是不用每一次操作都實時把資料寫盤,就算crash後也可以通過redo log恢復,所以能夠實現快速響應SQL語句。

Crash Safe,指MySQL伺服器宕機重啟後,能夠保證:
1.所有已經提交的事務的資料仍然存在。
2.所有沒有提交的事務的資料自動回滾。

redo log的寫入方式

redo log包括兩部分內容,分別是記憶體中的日誌緩衝(redo log buffer)和磁碟上的日誌檔案(redo log file)。

mysql每執行一條DML語句,會先把記錄寫入redo log buffer,後續某個時間點再一次性將多個操作記錄寫到redo log file。這種先寫日誌,再寫磁碟的技術,就是WAL。

在計算機作業系統中,使用者空間(user space)下的緩衝區資料,一般是無法直接寫入磁碟的,必須經過作業系統核心空間緩衝區(即OS Buffer)。

  • 日誌最開始會寫入位於儲存引擎Innodb的redo log buffer,這個是在使用者空間完成的。
  • 然後再將日誌儲存到作業系統核心空間的緩衝區(OS buffer)中。
  • 最後,通過系統呼叫fsync(),從OS buffer寫入到磁碟上的redo log file中,完成寫入操作。這個寫入磁碟的操作,就叫做刷盤

我們可以發現,redo log buffer寫入到redo log file,是經過OS buffer中轉的。其實可以通過引數innodb_flush_log_at_trx_commit進行配置,引數值含義如下:

  • 0:稱為延遲寫,事務提交時不會將redo log buffer中日誌寫入到OS buffer,而是每秒寫入OS buffer並呼叫寫入到redo log file中。
  • 1:稱為實時寫,實時刷”,事務每次提交都會將redo log buffer中的日誌寫入OS buffer並儲存到redo log file中。(預設)
  • 2:稱為實時寫,延遲刷。每次事務提交寫入到OS buffer,然後是每秒將日誌寫入到redo log file。(跳過了redo log buffer)

安全性從0->2->1遞增

redo log的執行流程

我們來看下Redo log的執行流程,假設執行的SQL如下:

update T set a =1 where id =666

關於MySQL Server層及儲存引擎層的概念,見資料庫原理一---MySQL基本架構與索引

Redo log的執行流程如下:

  1. MySQL客戶端將請求語句update T set a =1 where id =666,發往MySQL Server層。

  2. MySQL Server 層接收到SQL請求後,對其進行分析、優化、執行等處理工作,將生成的SQL執行計劃發到InnoDb儲存引擎層執行。

  3. InnoDb儲存引擎層將a修改為1的這個操作記錄到記憶體中。

  4. 記錄到記憶體以後會修改redo log 的記錄,會在新增一行記錄,其內容是需要在哪個資料頁上做什麼修改。

  5. 此後,將事務的狀態設定為prepare ,說明已經準備好提交事務了。

  6. 等到MySQL Server層處理完事務以後,會將事務的狀態設定為commit,也就是提交該事務。

  7. 在收到事務提交的請求以後,redo log會把剛才寫入記憶體中的操作記錄寫入到磁碟中,從而完成整個日誌的記錄過程。

Q&A

Q: redo log 為什麼可以保證crash safe機制呢?

  • 因為redo log每次更新操作完成後,就一定會寫入的,如果寫入失敗,說明此次操作失敗,事務也不可能提交。(注意上文redo log執行流程,在事務提交之前,就已經完成了redo log的更新)
  • redo log內部結構是基於頁的,記錄了這個頁的欄位值變化,只要crash後讀取redo log進行重放,就可以恢復資料。

歸檔日誌bin log

  • bin log是歸檔日誌,屬於MySQL Server層的日誌。可以實現主從複製資料恢復兩個作用。

  • 當需要恢復資料時,可以取出某個時間範圍內的bin log進行重放恢復。

  • 但是bin log不可以做crash safe,因為crash之前,bin log可能沒有寫入完全MySQL就掛了。所以需要配合redo log才可以進行crash safe。

bin log & redo log

redo log bin log
作用 用於崩潰恢復 主從複製和資料恢復
實現方式 Innodb儲存引擎實現 Server層實現,所有的儲存引擎都可以使用binlog日誌
記錄方式 迴圈寫的方式記錄,寫到結尾時,會回到開頭迴圈寫日誌 通過追加的方式記錄,當檔案尺寸大於給配置值後,後續的日誌會記錄到新的檔案上
檔案大小 redo log的大小是固定的 通過配置引數max_binlog_size設定每個binlog檔案大小
crash-safe能力 具有 沒有
日誌型別 物理日誌 邏輯日誌

(原文中這裡redo log是邏輯日誌,bin log是物理日誌,這裡更正一下:redo log 是物理日誌,記錄的是“在某個資料頁上做了什麼修改”;binlog 是邏輯日誌,記錄的是這個語句的原始邏輯)

如果資料庫誤操作, 如何執行資料恢復?
資料庫在某個時候誤操作,就可以找到距離誤操作最近的時間節點的bin log,重放到臨時資料庫裡,然後選擇誤刪的資料節點,恢復到線上資料庫。

MySQL二階段提交

MySQL二階段提交不同於從本地事務到分散式事務,淺談事務的分散式一致性演算法提到的二階段提交,那篇博文中的二階段提交指的是分散式事務的二階段提交,是多節點下的資料同步,而此處的二階段提交是指單節點下事務提交的兩階段。

  • redo log在寫入後,進入prepare狀態

  • 執行器寫入bin log

  • 進入commit狀態,事務可以提交。

為什麼需要兩階段提交呢?

  • 如果不用兩階段提交的話,可能會出現這樣情況:bin log寫入之前,機器crash導致需要重啟。重啟後redo log繼續重放crash之前的操作,而當bin log後續需要作為備份恢復時,會出現資料不一致的情況。

  • 如果是bin log commit之前crash,那麼重啟後,發現redo log是prepare狀態且bin log完整(bin log寫入成功後,redo log會有bin log的標記),就會自動commit,讓儲存引擎提交事務。

  • 兩階段提交就是為了保證redo log和binlog資料的安全一致性。只有在這兩個日誌檔案邏輯上高度一致了。你才能放心的使用redo log幫你將資料庫中的狀態恢復成crash之前的狀態,使用binlog實現資料備份、恢復、以及主從複製。

如果不是兩階段提交, 先寫redo log和先寫bin log兩種情況各會遇到什麼問題?

  • 先寫redo log,crash後bin log備份恢復時少了一次更新,與當前資料不一致。

  • 先寫bin log,crash後,由於redo log沒寫入,事務無效,所以後續bin log備份恢復時,資料不一致。

bin log刷盤機制

所有未提交的事務產生的binlog,都會被先記錄到binlog的快取中。等該事務提交時,再將快取中的資料寫入binlog日誌檔案中。快取的大小由引數binlog_chache_size控制。
binlog什麼時候重新整理到磁碟呢?由引數sync_binlog控制

  • 當sync_binlog為0時,表示MySQL不控制binlog的重新整理,而是由系統自行判斷何時寫入磁碟。選這種策略,一旦作業系統宕機,快取中的binlog就會丟失。

  • sync_binlog為N時,每N個事務,才會將binlog寫入磁碟。。

  • 當sync_binlog為1時,則表示每次commit,都將binlog 寫入磁碟。

bin log日誌三種格式

binlog日誌有三種格式

  • Statement:基於SQL語句的複製((statement-based replication,SBR))

  • Row:基於行的複製。(row-based replication,RBR)

  • Mixed:混合模式複製。(mixed-based replication,MBR)

Statement格式
每一條會修改資料的sql都會記錄在binlog中

  • 優點:不需要記錄每一行的變化,減少了binlog日誌量,節約了IO,提高效能。

  • 缺點:由於記錄的只是執行語句,為了這些語句能在備庫上正確執行,還必須記錄每條語句在執行的時候的一些相關資訊,以保證所有語句能在備庫得到和在主庫端執行時候相同的結果。

Row格式
不記錄sql語句上下文相關資訊,僅儲存哪條記錄被修改。

  • 優點:binlog中可以不記錄執行的sql語句的上下文相關的資訊,僅需要記錄那一條記錄被修改成什麼了。所以rowlevel的日誌內容會非常清楚的記錄下每一行資料修改的細節。不會出現某些特定情況下的儲存過程、或function、或trigger的呼叫和觸發無法被正確複製的問題。

  • 缺點:可能會產生大量的日誌內容。

Mixed格式
實際上就是Statement與Row的結合。一般的語句修改使用statment格式儲存binlog,如一些函式,statement無法完成主從複製的操作,則採用row格式儲存binlog,MySQL會根據執行的每一條具體的sql語句來區分對待記錄的日誌形式

MySQL主從複製

什麼是MySQL的主從複製
MySQL 主從複製是指資料可以從一個MySQL資料庫伺服器主節點複製到一個或多個從節點。MySQL 預設採用非同步複製方式,這樣從節點不用一直訪問主伺服器來更新自己的資料,資料的更新可以在遠端連線上進行,從節點可以複製主資料庫中的所有資料庫或者特定的資料庫,或者特定的表。

原理:

  1. master伺服器將資料的改變記錄二進位制binlog日誌,當master上的資料發生改變時,則將其改變寫入二進位制日誌中
  2. slave伺服器會在一定時間間隔內對master二進位制日誌進行探測其是否發生改變,如果發生改變,則開始一個I/OThread請求master二進位制事件
  3. 同時主節點為每個I/O執行緒啟動一個dump執行緒,用於向其傳送二進位制事件,並儲存至從節點本地的中繼日誌中,從節點將啟動SQL執行緒從中繼日誌中讀取二進位制日誌,在本地重放,使得其資料和主節點的保持一致,最後I/OThread和SQLThread將進入睡眠狀態,等待下一次被喚醒。

也就是說:

  • 從庫會生成兩個執行緒,一個I/O執行緒,一個SQL執行緒;

  • I/O執行緒會去請求主庫的binlog,並將得到的binlog寫到本地的relay-log(中繼日誌)檔案中;

  • 主庫會生成一個log dump執行緒,用來給從庫I/O執行緒傳binlog;

  • SQL執行緒,會讀取relay log檔案中的日誌,並解析成sql語句逐一執行;

回滾日誌undo log

  • undo log 叫做回滾日誌,用於記錄資料被修改前的資訊。

  • 它跟redo log重做日誌所記錄的相反,重做日誌記錄資料被修改後的資訊。undo log主要記錄的是資料的邏輯變化,為了在發生錯誤時回滾之前的操作,需要將之前的操作都記錄下來,這樣發生錯誤時才可以回滾。

  • undo log和redo log記錄物理日誌不一樣,它是邏輯日誌。可以認為當delete一條記錄時,undo log中會記錄一條對應的insert記錄,反之亦然,當update一條記錄時,它記錄一條對應相反的update記錄。

  • 當執行rollback時,就可以從undo log中的邏輯記錄讀取到相應的內容並進行回滾。有時候應用到行版本控制的時候,也是通過undo log來實現的:當讀取的某一行被其他事務鎖定時,它可以從undo log中分析出該行記錄以前的資料是什麼,從而提供該行版本資訊,讓使用者實現非鎖定一致性讀取。(MVCC)

MySQL事務提交過程

參考連結

MySQL日誌15連問---撿田螺的小男孩
mysql主從複製原理-binlog---低調人生