硬核乾貨!一文掌握 binlog 、redo log、undo log
在MySQL 中我們經常會接觸到三個核心日誌,它們分別是:binlog 、redo log、undo log。
好多同學對於它們可能並不陌生,但是具體區分起來各自的功能用途以及實現原理,那可能認知就會比較模糊了,今天就跟大家一起,來清晰明瞭的介紹一下這些日誌的核心思想和功能原理。
1 binlog
1.1 binlog 設計目標
binlog 記錄了對MySQL資料庫執行更改的所有的寫操作,包括所有對資料庫的資料、表結構、索引等等變更的操作。
注意:這其中不包含SELECT、SHOW等,因為對資料沒有修改
只要是對資料庫有變更的操作都會記錄到binlog裡面來,我們可以把資料庫的資料看做銀行賬戶裡的餘額,而binlog就相當於我們銀行卡的流水記錄。賬戶餘額只是一個結果,至於這個結果怎麼來的,那就必須得看流水了。
在實際應用中, binlog 的主要應用場景分別是 主從複製 和 資料恢復。
主從複製 :在 Master 端開啟 binlog ,然後將 binlog 傳送到各個 Slave 端, Slave 端重放 binlog 來達到主從資料一致。
資料恢復 :透過使用 mysqlbinlog 工具來恢復資料。
1.2 binlog 資料格式
binlog 日誌有三種格式,分別為 STATMENT 、 ROW 和 MIXED。
在 MySQL 5.7.7 之前,預設的格式是 STATEMENT , MySQL 5.7.7 之後,預設值是 ROW。日誌格式透過 binlog-format 指定。
ROW:基於行的複製(row-based replication, RBR),不記錄每條SQL語句的上下文資訊,僅需記錄哪條資料被修改了。如果一個update語句修改一百行資料,那麼這種模式下就會記錄100行對應的記錄日誌。
優點:不會出現某些特定情況下的儲存過程、或function、或trigger的呼叫和觸發無法被正確複製的問題;
缺點:會產生大量的日誌,尤其是 alter table 的時候會讓日誌暴漲。
STATMENT:基於SQL語句的複製( statement-based replication, SBR ),每一條會修改資料的SQL語句會記錄到 binlog 中 。相對於ROW模式,STATEMENT模式下只會記錄這個 update 的語句,所以此模式下會非常節省日誌空間,也避免著大量的IO操作。
優點:不需要記錄每一行的變化,減少了 binlog 日誌量,節約了 IO , 從而提高了效能;
缺點:在某些情況下會導致主從資料不一致,比如執行sysdate() 、 slepp() 等 。
MIXED:基於 STATMENT 和 ROW 兩種模式的混合複製(mixed-based replication, MBR),一般的複製使用 STATEMENT 模式儲存 binlog ,對於一些函式,STATEMENT 模式無法複製的操作使用 ROW 模式儲存 binlog。
基於這三種模式需要注意的是:
1)使用 row 格式的 binlog 時,在進行資料同步或恢復的時候不一致的問題更容易被發現,因為它是基於資料行記錄的。
2)使用 mixed 或者 statement 格式的 binlog 時,很多事務操作都是基於SQL邏輯記錄,我們都知道一個SQL在不同的時間點執行它們產生的資料變化和影響是不一樣的,所以這種情況下,資料同步或恢復的時候就容易出現不一致的情況。
1.3 binlog 寫入策略
對於 InnoDB 儲存引擎而言,在進行事務的過程中,首先會把binlog 寫入到binlog cache中(因為寫入到cache中會比較快,一個事務通常會有多個操作,避免每個操作都直接寫磁碟導致效能降低),只有在事務提交時才會記錄 biglog ,此時記錄還在記憶體中,那麼 biglog 是什麼時候刷到磁碟中的呢?
MySQL 其實是透過 sync_binlog 引數控制 biglog 的刷盤時機,取值範圍是 0-N:
0:每次提交事務binlog不會馬上寫入到磁碟,而是先寫到page cache。不去強制要求,由系統自行判斷何時寫入磁碟,在Mysql 崩潰的時候會有丟失日誌的風險;
1:每次提交事務都會執行 fsync 將 binlog 寫入到磁碟;
N:每次提交事務都先寫到page cach,只有等到積累了N個事務之後才 fsync 將 binlog 寫入到磁碟,在 MySQL 崩潰的時候會有丟失N個事務日誌的風險。
很顯然三種模式下,sync_binlog=1 是強一致的選擇,選擇0或者N的情況下在極端情況下就會有丟失日誌的風險,具體選擇什麼模式還是得看系統對於一致性的要求。
2、redo log
2.1 redo log 設計目標
redo log 是屬於引擎層(innodb)的日誌,稱為重做日誌 ,當MySQL伺服器意外崩潰或者當機後,保證已經提交的事務持久化到磁碟中(永續性)。
它能保證對於已經COMMIT的事務產生的資料變更,即使是系統當機崩潰也可以透過它來進行資料重做,達到資料的永續性,一旦事務成功提交後,不會因為異常、當機而造成資料錯誤或丟失。
2.2 redo log 資料格式
redo log 包括兩部分:
記憶體中的日誌緩衝(redo log buffer)
記憶體層面,預設16M,透過innodb_log_buffer_size引數可修改
磁碟上的日誌檔案(redo logfile)
持久化的,磁碟層面
MySQL 每執行一條 DML 語句,先將記錄寫入 redo log buffer,後續某個時間點再一次性將多個操作記錄寫到 redo log file。
通常所說的Write-Ahead Log(預先日誌持久化)指的是在持久化一個資料頁之前,先將記憶體中相應的日誌頁持久化。
在計算機作業系統中,使用者空間( user space )下的緩衝區資料一般情況下是無法直接寫入磁碟的,中間必須經過作業系統核心空間( kernel space )緩衝區( OS Buffer )。
因此, redo log buffer 寫入 redo logfile 實際上是先寫入 OS Buffer ,然後再透過系統呼叫 fsync() 將其刷到 redo log file中,過程如下:
修改資料的操作流程:
先將原始資料從磁碟中讀入記憶體中來,修改資料的記憶體複製,產生髒資料
生成一條重做日誌並寫入redo log buffer,記錄的是資料被修改後的值
預設在事務提交後將redo log buffer中的內容重新整理到redo log file,對redo log file採用追加寫的方式
定期將記憶體中修改的資料重新整理到磁碟中(這裡說的是那些還沒及時被後臺執行緒刷盤的髒資料)
2.3 關於 redo log 的幾點疑惑
讀到這裡,相必有同學會有如下疑問:
Q1:為什麼不直接修改磁碟中的資料?
因為直接修改磁碟資料的話,它是隨機IO,修改的資料分佈在磁碟中不同的位置,需要來回的查詢,所以命中率低,消耗大,而且一個小小的修改就不得不將整個頁重新整理到磁碟,利用率低;
與之相對的是順序IO,磁碟的資料分佈在磁碟的一塊,所以省去了查詢的過程,節省尋道時間。
使用後臺執行緒以一定的頻率去重新整理磁碟可以降低隨機IO的頻率,增加吞吐量,這是使用buffer pool的根本原因。
Q2:同為運算元據變更的日誌,有了binlog為什麼還要redo log?
我認為最核心的一點就是兩者記錄的資料變更粒度是不一樣的。
以修改資料為例,binlog 是以表為記錄主體,在ROW模式下,binlog儲存的表的每行變更記錄。
MySQL 是以頁為單位進行刷盤的,每一頁的資料單位為16K,所以在刷盤的過程中需要把資料重新整理到磁碟的多個扇區中去。而把16K資料刷到磁碟的每個扇區裡這個過程是無法保證原子性的,如果資料庫當機,那麼就可能會造成一部分資料成功,而一部分資料失敗的情況。而透過 binlog 這種級別的日誌是無法恢復的,因為一個update可能更改了多個磁碟區域的資料,所以這個時候得需要透過redo log這種記錄到磁碟資料級別的日誌進行資料恢復。
由以上兩者的對比可知:binlog 日誌只用于歸檔,只依靠 binlog 是沒有 crash-safe 能力的。
同樣只有 redo log 也不行,因為 redo log 是 InnoDB特有的,且日誌上的記錄落盤後會被覆蓋掉。因此需要 binlog和 redo log二者同時記錄,才能保證當資料庫發生當機重啟時,資料不會丟失。
Q3:redo log一定能保證事務的永續性嗎?
不一定,這要根據redo log的刷盤策略決定,因為redo log buffer同樣是在記憶體中,如果提交事務之後,redo log buffer還沒來得及將資料重新整理到redo log file進行持久化,此時發生當機照樣會丟失資料。
那該如何解決呢?刷盤寫入策略。
2.4 redo log 寫入策略
當redo log空間滿了之後又會從頭開始以迴圈的方式進行覆蓋式的寫入。MySQL 支援三種將 redo log buffer 寫入 redo log file 的時機,可以透過 innodb_flush_log_at_trx_commit 引數配置,各引數含義如下:
0(延遲寫):表示每次事務提交時都只是把 redo log 留在 redo log buffer 中,開啟一個後臺執行緒,每1s重新整理一次到磁碟中 ;
1(實時寫,實時刷):表示每次事務提交時都將 redo log 直接持久化到磁碟,真正保證資料的永續性;
2(實時寫,延遲刷):表示每次事務提交時都只是把 redo log 寫到 page cache,具體的刷盤時機不確定。
除了上面幾種機制外,還有其它兩種情況會把redo log buffer中的日誌刷到磁碟。
定時處理:有執行緒會定時(每隔 1 秒)把redo log buffer中的資料刷盤。
根據空間處理:redo log buffer 佔用到了一定程度( innodb_log_buffer_size 設定的值一半)佔,這個時候也會把redo log buffer中的資料刷盤。
3、undo log
3.1 undo log設計目標
redo log 是也屬於引擎層(innodb)的日誌,從上面的redo log介紹中我們就已經知道了,redo log 和undo log的核心是為了保證innodb事務機制中的永續性和原子性,事務提交成功由redo log保證資料永續性,而事務可以進行回滾從而保證事務操作原子性則是透過undo log 來保證的。
原子性 是指對資料庫的一系列操作,要麼全部成功,要麼全部失敗,不可能出現部分成功的情況。
undo log 的主要應用場景分別:
事務回滾 :前面提到過,後臺執行緒會不定時的去重新整理buffer pool中的資料到磁碟,但是如果該事務執行期間出現各種錯誤(當機)或者執行rollback語句,那麼前面刷進去的操作都是需要回滾的,保證原子性,undo log就是提供事務回滾的。
MVCC:當讀取的某一行被其他事務鎖定時,可以從undo log中分析出該行記錄以前的資料版本是怎樣的,從而讓使用者能夠讀取到當前事務操作之前的資料——快照讀。
3.2 undo log 資料格式
undo log 資料主要分兩類:
insert undo log
insert 操作的記錄,只對事務本身可見,對其他事務不可見(這是事務隔離性的要求),故該undo log可以在事務提交後直接刪除,不需要進行purge操作。
update undo log
update undo log記錄的是對delete和update操作產生的undo log。該undo log可能需要提供MVCC機制,因此不能在事務提交時就進行刪除。提交時放入undo log連結串列,等待purge執行緒進行最後的刪除。
在InnoDB儲存引擎中,undo log使用rollback segment回滾段進行儲存,每隔回滾段包含了1024個undo log segment。MySQL5.5之後,一共有128個回滾段。即總共可以記錄128 * 1024個undo操作。
每個事務只會使用一個回滾段,一個回滾段在同一時刻可能會服務於多個事務。
3.3 undo log 操作例項
1、首先準備一張原始原始資料表(user_info)
對於InnoDB引擎來說,每個行記錄除了記錄本身的資料之外,還有幾個隱藏的列:
DB_ROW_ID∶記錄的主鍵id。
DB_TRX_ID:事務ID,當對某條記錄發生修改時,就會將這個事務的Id記錄其中。
DB_ROLL_PTR︰回滾指標,版本鏈中的指標。
2、開啟一個事務A
對 user_info 表執行如下SQL:
update user_info set name =“李四”where id=1
將會進行如下流程操作:
1、首先獲得一個事務編號 104
2、把user_info表修改前的資料複製到undo log
3、修改user_info表 id=1的資料
4、把修改後的資料事務版本號改成 當前事務版本號,並把DB_ROLL_PTR 地址指向undo log資料地址。
3、最後執行結束
結果如下所示:
可以發現每次對資料的變更都會產生一個undo log,當一條記錄被變更多次時,那麼就會產生多條undo log,undo log記錄的是變更前的日誌,並且每個undo log的序號是遞增的,那麼當要回滾的時候,按照序號依次向前推,就可以找到我們的原始資料了。
總結
binlog 是MySQL server層的日誌,而redo log 和undo log都是引擎層(InnoDB)的日誌,要換其他資料引擎那麼就未必有redo log和undo log了。
它的設計目標是支援innodb的“事務”的特性,事務ACID特性分別是原子性、一致性、隔離性、永續性, 一致性是事務的最終追求的目標,隔離性、原子性、永續性是達成一致性目標的手段,根據的之前的介紹我們已經知道隔離性是透過鎖機制來實現的,而事務的原子性和永續性則是透過redo log 和undo log來保障的。
寫入策略
事務執行過程中,先把日誌寫到bin log cache ,事務提交的時候,再把binlog cache寫到binlog檔案中。因為一個事務的binlog不能被拆開,無論這個事務多大,也要確保一次性寫入,所以系統會給每個執行緒分配一個塊記憶體作為binlog cache。
binlog vs redo log
redo log 物理日誌:記錄內容是“在xx資料頁做了xx修改”,屬於InnoDB儲存引擎層產生的。
binlog 邏輯日誌:記錄內容是語句的原始邏輯,類似於給ID=2這一行的c欄位加1,屬於服務層。
兩個側重點也不同, redo log讓InnoDB有了崩潰恢復的能力,binlog保證了MySQL叢集架構的資料一致性。
在執行更新語句過程,會記錄redo log與binlog兩塊日誌,以基本的事務為單位,redo log在事務執行過程中可以不斷寫入,而binlog只有在提交事務時才寫入,所以redo log與binlog的寫入時機不一樣。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024923/viewspace-2927186/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- MySQL中redo log、undo log、binlog關係以及區別MySql
- mysql日誌:redo log、binlog、undo log 區別與作用MySql
- 深入理解MySQL系列之redo log、undo log和binlogMySql
- undo log和redo log
- MySQL中的redo log和undo logMySql
- MySQL Undo Log和Redo Log介紹MySql
- 必須瞭解的mysql三大日誌-binlog、redo log和undo logMySql
- 【Mysql】三大日誌 redo log、bin log、undo logMySql
- 3000幀動畫圖解MySQL為什麼需要binlog、redo log和undo log動畫圖解MySql
- 2 萬字 + 30 張圖 | 細聊 MySQL undo log、redo log、binlog 有什麼用?MySql
- MySQL 日誌系統 redo log、binlogMySql
- 基於Redo Log和Undo Log的MySQL崩潰恢復流程MySql
- redo log 和 binlog 的一些總結
- MySQL的Redo log 以及Bin logMySql
- InnoDB undo log原理
- mysql之 redo logMySql
- MYSQL 是如何保證binlog 和redo log同時提交的?MySql
- 一生摯友redo log、binlog《死磕MySQL系列 二》MySql
- MySQL更新資料時,日誌(redo log、binlog)執行流程MySql
- Sqlserver沒有單獨的undo檔案,使用tempdb和redo log來存放undo資料SQLServer
- MySQL redo log最佳化MySql
- MySQL重做日誌(redo log)MySql
- redo log file 最佳化
- binlog_rows_query_log_events
- MySQL中的redo log和checkpointMySql
- How to Dump Redo Log File Information --metalinkORM
- InnoDB文件筆記(二)—— Redo Log筆記
- InnoDB文件筆記(三)—— Undo Log筆記
- MySQL必知必會:簡介undo log、truncate、以及undo log如何幫你回滾事物MySql
- MySQL中undo log介紹及清理MySql
- InnoDB purge原理--哪些undo log可purge
- Oracle redo解析之-1、oracle redo log結構計算Oracle Redo
- 資料庫篇:mysql日誌型別之 redo、undo、binlog資料庫MySql型別
- MySQL學習之change buffer 和 redo logMySql
- MySQL如何計算統計redo log大小MySql
- Oracle Redo and UndoOracle Redo
- 【REDO】Oracle redo undo 學習Oracle Redo
- MySQL Redo log頁內邏輯怎麼理解MySql