MySQL 的 binlog 和 redo log 寫入原理

MySQL 的 binlog 和 redo log 寫入原理

binlog 的寫入流程

事務執行過程中,binlog 首先會被寫到 binlog cache 中;事務提交的時候,再講binlog cache 寫到 binlog 文件中。一個事務的 binlog 是原子的,無論多大都需要保證完整性。

系統為每個客戶端線程分配一個 binlog cache,其大小由 binlog_cache_size 控制。如果binlog cache 超過閥值,就會臨時持久化到磁盤。當事務提交的時候,再將 binlog cache 中完整的事務持久化到磁盤中,並清空 binlog cache。

MySQL 的 binlog 和 redo log 寫入原理

從上面可以看出,每個客戶端線程都有自己獨立的 binlog cache,但是會共享一份 binlog files。

上面的 write 是指把binlog cache 寫到文件系統的 page cache,並沒有寫入到磁盤中,因此速度較快。

fsync 是實際的寫盤操作,佔用磁盤的 IOPS。

write 和 fsync 的寫入時機,是由sync_binlog 控制的:

  1. sync_binlog=0:每次事務提交都只 write,不 fsync;
  2. sync_binlog=1:每次事務提交都會fsync;
  3. sync_binlog=N(N>1):每次提交事務都會 write,累計N 個後再執行 fsync。

在出現 IO 瓶頸的情況下,可以考慮將 sync_binlog 設置成一個大的值。比較常見的是將 N設置為 100~1000。但是存在的風險是,當主機異常重啟時會丟失 N 個最近提交的事務 binlog。

redo log 寫入流程

前面介紹過了 redo log 的寫入首先會寫入 redo log cache,其詳細的狀態如下所示:

MySQL 的 binlog 和 redo log 寫入原理

redo log 對應上面的 3 種狀態分別是:1、在 MySQL 應用的 redo log buffer 中;2、write 到文件系統的 page cache 中,但是沒有進行實際的寫盤操作(fsync);3、執行 fsync 之後,寫盤結束。

InnoDB 有一個後臺線程,每個 1 秒鐘 就會將 redo log buffer 中的日誌,調用 write 寫入到 文件系統的 page cache 中,然後再調用 fsync 持久化到磁盤中。redo log buffer 是共享的,因此一些正在執行中的事務的 redo log 也有可能被持久化到磁盤中。

通常我們說的 MySQL 的 “雙1” 操作,指的是 sync_binlog = 1 AND innodb_flush_log_at_trx_commit = 1 。innodb_flush_log_at_trx_commit 設置成 1 表示 redo log 在 prepare 階段就需要持久化一次,那麼 “雙1” 配置 每個事務提交的時候都會刷盤 2 次,一次是 binlog,一次是 redo log。

為了控制 redo log 的寫入策略,innodb_flush_log_at_trx_commit 會有下面 3 中取值:1、0:每次提交事務只寫在 redo log buffer 中;2、1:每次提交事務持久化到磁盤;3、2:每次提交事務寫到 文件系統的 page cache 中。

redo log 實際的觸發 fsync 操作寫盤包含以下幾個場景:1、後臺每隔 1 秒鐘的線程輪詢;2、innodb_flush_log_at_trx_commit 設置成 1 時,事務提交時觸發;3、innodb_log_buffer_size 是設置 redo log 大小的參數,當 redo log buffer 達到 innodb_log_buffer_size / 2 時,也會觸發一次 fsync。

組提交

MySQL 為了優化磁盤持久化的開銷,會有一個 組提交(group commit)的機制。首先我們介紹一下 日誌邏輯序列號(log sequence number,LSN),它是用來對應每個 redo log 寫入點的遞增序列號。每次寫入長度為 length 的 redo log,LSN 就會加上 length。

下面是 3 個併發事務(trx1、trx2、trx3)在 prepare 階段都寫完了 redo log buffer,然後組提交持久化的過程。其中 3 個事務對應的 LSN 分別是:50、120、160。

MySQL 的 binlog 和 redo log 寫入原理

  1. trx1 是最先到達的,會被選為組 leader;
  2. 當 trx1 準備提交的時候,組裡面已經有 3 個事務了,此時 LSN 變成了 160;
  3. trx1 寫盤結束後,LSN 小於 160 的區塊都已經被持久化;
  4. 此時 trx2 和 trx3 就可以直接返回了。

組提交優化

從上面描述可以看出,一次組提交裡面的組員越多,節約磁盤 IOPS 的效果越好。在併發場景下,一個事務寫完 redo log 之後,fsnyc 越晚調用,組員可能越多,其節約IOPS 的效果越好。binlog 和 redo log 的執行過程簡圖如下所示:

MySQL 的 binlog 和 redo log 寫入原理

上面 binlog 的寫入是 1 個步驟,事實上 binlog 的寫入也是分成 2 步的:1、先將 binlog 從 binlog cache 寫入磁盤的 page cache;2、然後再調用 fsync 持久化。為了讓組提交的效果更好,把 redo log 做 fsync 的時間調整到了 binlog write 之後,如下所示:

MySQL 的 binlog 和 redo log 寫入原理

從上圖的流程可以看出,binlog 的寫盤也可以組提交了。當上面執行 binlog: fsync 時,可以將需要寫盤的 binlog 一起寫入(事務完整 binlog),這樣也可以減少一部分 IOPS 的開銷。

通常情況下 redo log prepare: fsync 階段執行的時間較短,此時可能binlog 的組提交可能沒有 redo log 的組提交效果那麼好。此時可以通過下面 2 個參數來提升 binlog 組提交的效率:

  1. binlog_group_commit_sync_delay:表示延遲多少微秒後,再執行 fsync;
  2. binlog_group_commit_sync_no_delay_count:表示累計多少次後,在調用 fsync;

上面 2 個參數是 或 的關係,滿足其中一個就可以觸發 fsync。

WAL 機制可以減少磁盤的寫入次數,主要得益於下面 2 個方面:

1、redo log 和 binlog 都是順序寫,比磁盤的隨機寫要快;2、組提交機制可以大幅度減少磁盤的IOPS 消耗。

總結

當MySQL 出現了 IO 上面的性能問題,可以考慮下面的優化策略。

  1. 設置 binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count。可以使用故意等待來減少,binlog 的寫盤次數,沒有數據丟失的風險,但是會有客戶端響應變慢的風險。
  2. 設置 sync_binlog 設置為 100~1000 之間的某個值。這樣做存在的風險是可能造成 binlog 丟失。
  3. 設置 innodb_flush_log_at_trx_commit = 2,可能會丟數據。

參考:《極客時間:MySQL實戰》、《高性能MySQL》


分享到:


相關文章: