天天看點

【mysql45講】redo log & binlog1.WAL2.redo log介紹3.binlog介紹4.redo log和binlog的差別5.兩階段送出(使用redo log和binlog)6.MySQL 的“雙 1”配置7.組送出機制8.如果MySQL 現在出現了性能瓶頸,而且瓶頸在 IO 上,可以通過哪些方法來提升性能呢?

1.WAL

WAL 的全稱是 Write-Ahead Logging,它的關鍵點就是先寫日志(寫日志也是寫磁盤,不過是順序寫,是以快),再寫磁盤。

隻要 redo log 和 binlog 保證持久化到磁盤,就能確定 MySQL 異常重新開機後,資料可以恢複。

WAL 機制主要得益于兩個方面:

redo log 和 binlog 都是順序寫,磁盤的順序寫比随機寫速度要快;

組送出機制,可以大幅度降低磁盤的 IOPS 消耗。

2.redo log介紹

2.1 redo log循環寫

redo log屬于InnoDB日志。

redo log 是固定大小的,比如可以配置為一組 4 個檔案,每個檔案的大小是 1GB,那麼總共就可以記錄 4GB 的操作。從頭開始寫,寫到末尾就又回到開頭循環寫。

示例如圖:

【mysql45講】redo log & binlog1.WAL2.redo log介紹3.binlog介紹4.redo log和binlog的差別5.兩階段送出(使用redo log和binlog)6.MySQL 的“雙 1”配置7.組送出機制8.如果MySQL 現在出現了性能瓶頸,而且瓶頸在 IO 上,可以通過哪些方法來提升性能呢?

write pos 是目前記錄的位置,一邊寫一邊後移,寫到第 3 号檔案末尾後就回到 0 号檔案開頭。checkpoint 是目前要擦除的位置,也是往後推移并且循環的,擦除記錄前要把記錄更新到資料檔案。write pos 和 checkpoint 之間的是“粉闆”上還空着的部分,可以用來記錄新的操作。如果 write pos 追上 checkpoint,表示“粉闆”滿了,這時候不能再執行新的更新,得停下來先擦掉一些記錄,把 checkpoint 推進一下。

2.2 crash-safe

即使資料庫異常重新開機,之前的記錄也不會丢失,這就是redo log有crash-safe能力。

innodb_flush_log_at_trx_commit 這個參數設定成 1 的時候,表示每次事務的 redo log都直接持久化到磁盤。這個參數我建議你設定成 1,這樣可以保證 MySQL 異常重新開機之後資料不丢失。

2.3 redo log的寫入機制

redo log可能存在的三種狀态如下:

1.存在 redo log buffer 中,實體上是在 MySQL 程序記憶體中;

2.寫到磁盤 (write),但是沒有持久化(fsync),實體上是在檔案系統的 page cache 裡;

3.持久化到磁盤,對應的是 hard disk。

日志寫到 redo log buffer 是很快的,wirte 到 page cache 也差不多,但是持久化到磁盤的速度就慢多了。

為了控制 redo log 的寫入政策,InnoDB 提供了 innodb_flush_log_at_trx_commit 參數,它有三種可能取值:

1.設定為 0 的時候,表示每次事務送出時都隻是把 redo log 留在 redo log buffer 中 ;

2.設定為 1 的時候,表示每次事務送出時都将 redo log 直接持久化到磁盤;

3.設定為 2 的時候,表示每次事務送出時都隻是把 redo log 寫到 page cache。

沒有送出事務的redo log被寫入到磁盤的三種情況:

1.InnoDB 有一個背景線程,每隔 1 秒,就會把 redo log buffer 中的日志,調用 write 寫到檔案系統的 page cache,然後調用 fsync 持久化到磁盤。

2.redo log buffer 占用的空間即将達到 innodb_log_buffer_size 一半的時候,背景線程會主動寫盤。注意,由于這個事務并沒有送出,是以這個寫盤動作隻是 write,而沒有調用 fsync,也就是隻留在了檔案系統的 page cache。

3.并行的事務送出的時候,順帶将這個事務的 redo log buffer 持久化到磁盤。假設一個事務 A 執行到一半,已經寫了一些 redo log 到 buffer 中,這時候有另外一個線程的事務 B 送出,如果 innodb_flush_log_at_trx_commit 設定的是 1,那麼按照這個參數的邏輯,事務 B 要把 redo log buffer 裡的日志全部持久化到磁盤。這時候,就會帶上事務 A 在 redo log buffer 裡的日志一起持久化到磁盤。

2.4 redo log的目的

確定事務的持久性。防止在發生故障的時間點,尚有髒頁未寫入磁盤,在重新開機mysql服務的時候,根據redo log進行重做,進而達到事務的持久性這一特性。

3.binlog介紹

3.1 binlog

binlog屬于Server層的日志(歸檔日志)。

它可以追加寫入,不會覆寫之前的。

隻依靠binlog是沒有crash-safe能力的。

sync_binlog 這個參數設定成 1 的時候,表示每次事務的 binlog 都持久化到磁盤。這個參數我也建議你設定成 1,這樣可以保證 MySQL 異常重新開機之後 binlog 不丢失。

3.2 binlog的寫入機制

事務執行過程中,先把日志寫到 binlog cache,事務送出的時候,再把 binlog cache 寫到 binlog 檔案中。

一個事務的 binlog 是不能被打斷的,一個事務的binlog必須連續寫。這就涉及到了 binlog cache 的儲存問題。系統給 binlog cache 配置設定了一片記憶體,每個線程一個,

參數 binlog_cache_size 用于控制單個線程内 binlog cache 所占記憶體的大小。如果超過了這個參數規定的大小,就要暫存到磁盤。事務送出的時候,執行器把 binlog cache 裡的完整事務寫入到 binlog 中,并清空 binlog cache。

【mysql45講】redo log & binlog1.WAL2.redo log介紹3.binlog介紹4.redo log和binlog的差別5.兩階段送出(使用redo log和binlog)6.MySQL 的“雙 1”配置7.組送出機制8.如果MySQL 現在出現了性能瓶頸,而且瓶頸在 IO 上,可以通過哪些方法來提升性能呢?

可以看到,每個線程有自己 binlog cache,但是共用同一份 binlog 檔案。

圖中的 write,指的就是指把日志寫入到檔案系統的 page cache,并沒有把資料持久化到磁盤,是以速度比較快。

圖中的 fsync,才是将資料持久化到磁盤的操作。一般情況下,我們認為 fsync 才占磁盤的 IOPS。

write 和 fsync 的時機,是由參數 sync_binlog 控制的:

sync_binlog=0 的時候,表示每次送出事務都隻 write,不 fsync;

sync_binlog=1 的時候,表示每次送出事務都會執行 fsync;

sync_binlog=N(N>1) 的時候,表示每次送出事務都 write,但累積 N 個事務後才 fsync。(如果主機發生異常重新開機,會丢失最近 N 個事務的 binlog 日志。)

是以,在出現 IO 瓶頸的場景裡,将 sync_binlog 設定成一個比較大的值,可以提升性能。在實際的業務場景中,考慮到丢失日志量的可控性,一般不建議将這個參數設成 0,比較常見的是将其設定為 100~1000 中的某個數值。

3.3 binlog的三種模式差別

1.Statement

每一條修改資料的 sql 都會記錄到 master 的 binlog 中,slave 在複制的時候,sql 程序會解析成和原來在 master 端執行時的相同的 sql 再執行。

2.Row

日志中會記錄每一行資料被修改的形式,然後在 slave 端再對相同的資料進行修改。row 模式隻記錄要修改的資料,隻有 value,不會有 sql 多表關聯的情況。

3.Mixed

MySQL 會根據執行的每一條具體的 SQL 語句來區分對待記錄的日志形式,也就是在 statement 和 row 之間選擇一種。

3.4 binlog的目的

兩個作用:備份和複制。

binlog 用于主從複制中,從庫利用主庫上的 binlog 進行重播,實作主從同步。用于資料庫的基于時間點、位點等的還原操作。

4.redo log和binlog的差別

1.redo log是InnoDB引擎特有的,而binlog屬于Server層所有引擎都能用。

2.redo log是實體日志,記錄的是“在某個資料頁上實作了什麼修改”。binlog是邏輯日志,記錄的是這個語句的原始邏輯,比如“給 ID=2 這一行的 c 字段加 1 ”。

3.redo log循環寫,空間固定會用完。binlog追加寫,檔案寫到一定大小會切換下一個,不會覆寫以前的日志。

5.兩階段送出(使用redo log和binlog)

5.1 兩階段送出介紹

比如執行下面這條語句

mysql> update T set c=c+1 where ID=2;

過程如圖

【mysql45講】redo log & binlog1.WAL2.redo log介紹3.binlog介紹4.redo log和binlog的差別5.兩階段送出(使用redo log和binlog)6.MySQL 的“雙 1”配置7.組送出機制8.如果MySQL 現在出現了性能瓶頸,而且瓶頸在 IO 上,可以通過哪些方法來提升性能呢?

寫入redo log分為:prepare和commit兩個步驟

兩階段送出過程:寫入redo log(處于prepare狀态)->寫binlog到磁盤->redo log commit

兩階段送出目的:讓兩份日志邏輯一緻

5.2 兩階段送出如何保證資料庫異常重新開機時資料完整性

5.2.1 舉例

【mysql45講】redo log & binlog1.WAL2.redo log介紹3.binlog介紹4.redo log和binlog的差別5.兩階段送出(使用redo log和binlog)6.MySQL 的“雙 1”配置7.組送出機制8.如果MySQL 現在出現了性能瓶頸,而且瓶頸在 IO 上,可以通過哪些方法來提升性能呢?

A時刻異常重新開機:由于redo log未送出,binlog也沒寫,這個事務會復原。

B時刻異常重新開機:redo log寫完未送出,binlog已寫。對應以下的2(a)情況,崩潰恢複時事務會送出。

崩潰恢複時的判斷規則:

如果 redo log 裡面的事務是完整的,也就是已經有了 commit 辨別,則直接送出;

如果 redo log 裡面的事務隻有完整的 prepare,則判斷對應的事務 binlog 是否存在并完整:

a. 如果是,則送出事務;b. 否則,復原事務。

5.2.2 其他問題

1.怎樣知道binlog完整性

一個事務的 binlog 是有完整格式的:statement 格式的 binlog,最後會有 COMMIT;row 格式的 binlog,最後會有一個 XID event。

另外,在 MySQL 5.6.2 版本以後,還引入了 binlog-checksum 參數,用來驗證 binlog 内容的正确性。對于

binlog 日志由于磁盤原因,可能會在日志中間出錯的情況,MySQL 可以通過校驗 checksum 的結果來發現。是以,MySQL

還是有辦法驗證事務 binlog 的完整性的。

2.怎麼關聯redo log和binlog

redo log和binlog有個共同的字段XID。崩潰恢複的時候會按順序掃描redo log:

碰到有prepare,commit的redo log,直接送出;

碰到有prepare,沒commit的redo log,就拿着這個XID去找binlog。

3.隻有binlog為什麼不支援資料恢複

【mysql45講】redo log & binlog1.WAL2.redo log介紹3.binlog介紹4.redo log和binlog的差別5.兩階段送出(使用redo log和binlog)6.MySQL 的“雙 1”配置7.組送出機制8.如果MySQL 現在出現了性能瓶頸,而且瓶頸在 IO 上,可以通過哪些方法來提升性能呢?
【mysql45講】redo log & binlog1.WAL2.redo log介紹3.binlog介紹4.redo log和binlog的差別5.兩階段送出(使用redo log和binlog)6.MySQL 的“雙 1”配置7.組送出機制8.如果MySQL 現在出現了性能瓶頸,而且瓶頸在 IO 上,可以通過哪些方法來提升性能呢?

6.MySQL 的“雙 1”配置

sync_binlog 和 innodb_flush_log_at_trx_commit 都設定成 1。也就是說,一個事務完整送出前,需要等待兩次刷盤,一次是 redo log(prepare 階段),一次是 binlog。

7.組送出機制

7.1 日志邏輯序列号LSN

LSN 是單調遞增的,用來對應 redo log 的一個個寫入點。每次寫入長度為 length 的 redo log, LSN 的值就會加上 length。LSN 也會寫到 InnoDB 的資料頁中,來確定資料頁不會被多次執行重複的 redo log。

7.2 redo log組送出

舉例如圖:三個并發事務 (trx1, trx2, trx3) 在 prepare 階段,都寫完 redo log buffer,持久化到磁盤的過程,對應的 LSN 分别是 50、120 和 160。

【mysql45講】redo log & binlog1.WAL2.redo log介紹3.binlog介紹4.redo log和binlog的差別5.兩階段送出(使用redo log和binlog)6.MySQL 的“雙 1”配置7.組送出機制8.如果MySQL 現在出現了性能瓶頸,而且瓶頸在 IO 上,可以通過哪些方法來提升性能呢?
【mysql45講】redo log & binlog1.WAL2.redo log介紹3.binlog介紹4.redo log和binlog的差別5.兩階段送出(使用redo log和binlog)6.MySQL 的“雙 1”配置7.組送出機制8.如果MySQL 現在出現了性能瓶頸,而且瓶頸在 IO 上,可以通過哪些方法來提升性能呢?

是以,一次組送出裡面,組員越多,節約磁盤 IOPS 的效果越好。但如果隻有單線程壓測,那就隻能老老實實地一個事務對應一次持久化操作了。在并發更新場景下,第一個事務寫完 redo log buffer 以後,接下來這個 fsync 越晚調用,組員可能越多,節約 IOPS 的效果就越好。

7.3 mysql做的優化,使binlog也可以組送出

寫 binlog 是分成兩步的:先把 binlog 從 binlog cache 中寫到磁盤上的 binlog 檔案;調用 fsync 持久化。

MySQL 為了讓組送出的效果更好,把 redo log 做 fsync 的時間拖到了binlog write之後。也就是說,圖變成了這樣:

【mysql45講】redo log & binlog1.WAL2.redo log介紹3.binlog介紹4.redo log和binlog的差別5.兩階段送出(使用redo log和binlog)6.MySQL 的“雙 1”配置7.組送出機制8.如果MySQL 現在出現了性能瓶頸,而且瓶頸在 IO 上,可以通過哪些方法來提升性能呢?

這樣,binlog 也可以組送出了。在執行圖 中第 4 步把 binlog fsync 到磁盤時,如果有多個事務的 binlog 已經寫完了,也是一起持久化的,這樣也可以減少 IOPS 的消耗。不過通常情況下第 3 步執行得會很快,是以 binlog 的 write 和 fsync 間的間隔時間短,導緻能集合到一起持久化的 binlog 比較少,是以 binlog 的組送出的效果通常不如 redo log 的效果那麼好。

【mysql45講】redo log & binlog1.WAL2.redo log介紹3.binlog介紹4.redo log和binlog的差別5.兩階段送出(使用redo log和binlog)6.MySQL 的“雙 1”配置7.組送出機制8.如果MySQL 現在出現了性能瓶頸,而且瓶頸在 IO 上,可以通過哪些方法來提升性能呢?

8.如果MySQL 現在出現了性能瓶頸,而且瓶頸在 IO 上,可以通過哪些方法來提升性能呢?

【mysql45講】redo log & binlog1.WAL2.redo log介紹3.binlog介紹4.redo log和binlog的差別5.兩階段送出(使用redo log和binlog)6.MySQL 的“雙 1”配置7.組送出機制8.如果MySQL 現在出現了性能瓶頸,而且瓶頸在 IO 上,可以通過哪些方法來提升性能呢?