資料庫原理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的執行流程如下:
-
MySQL客戶端將請求語句update T set a =1 where id =666,發往MySQL Server層。
-
MySQL Server 層接收到SQL請求後,對其進行分析、優化、執行等處理工作,將生成的SQL執行計劃發到InnoDb儲存引擎層執行。
-
InnoDb儲存引擎層將a修改為1的這個操作記錄到記憶體中。
-
記錄到記憶體以後會修改redo log 的記錄,會在新增一行記錄,其內容是需要在哪個資料頁上做什麼修改。
-
此後,將事務的狀態設定為prepare ,說明已經準備好提交事務了。
-
等到MySQL Server層處理完事務以後,會將事務的狀態設定為commit,也就是提交該事務。
-
在收到事務提交的請求以後,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 預設採用非同步複製方式,這樣從節點不用一直訪問主伺服器來更新自己的資料,資料的更新可以在遠端連線上進行,從節點可以複製主資料庫中的所有資料庫或者特定的資料庫,或者特定的表。
原理:
- master伺服器將資料的改變記錄二進位制binlog日誌,當master上的資料發生改變時,則將其改變寫入二進位制日誌中
- slave伺服器會在一定時間間隔內對master二進位制日誌進行探測其是否發生改變,如果發生改變,則開始一個I/OThread請求master二進位制事件
- 同時主節點為每個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)