前言
本文介紹 MySQL 的 binlog 和 redo log 寫入機制和刷盤策略,以及如何提升 MySQL 的 IO 性能。
binlog 的寫入機制
binlog 的寫入流程是:
先將日志寫入到 binlog cache 中,然后再 write page cache 到磁盤(文件系統向內核申請的一塊內存空間,當 MySQL 進程重啟時,不影響這塊空間),最后 fsync 到磁盤。
每個事務對應的 binlog 是不能拆開的,所以必須一次性保存。每個線程有自己的 binlog cache,通過 binlog_cache_size 參數設置,但是多個線程共享同一個 binlog 文件。如果 binlog cache 存不下,就會存到磁盤中。
binlog 的刷盤策略,即 write 和 fsync 的時機,是由參數 sync_binlog
控制的:
-
sync_binlog=0的時候,表示每次提交事務都只write,不fsync;
-
sync_binlog=1的時候,表示每次提交事務都會執行fsync;
-
sync_binlog=N(N>1)的時候,表示每次提交事務都write,但累積N個事務后才fsync。
一般不會將 sync_binlog 設置為 0,那樣可能會丟失大量的日志。為了提高 IO 性能,可能會將 sync_binlog 設置為 100 ~ 1000 的值,這樣只會丟失最近的 N 條日志。
redo log 的寫入機制
redo log 的寫入流程:
先將日志記錄在 redo log buffer 中,然后再 write page cache,最后 fsync 到磁盤。並且,有一個后台線程,每隔一段時間就會將 redo log buffer 同步到磁盤。(可能事務未提交,但也同步到磁盤的情況)
redo log 的刷盤策略,由 InnoDB 提供了 innodb_flush_log_at_trx_commit
參數控制,它有三種可能取值:
-
設置為0的時候,表示每次事務提交時都只是把redo log留在redo log buffer中;
-
設置為1的時候,表示每次事務提交時都將redo log直接持久化到磁盤;
-
設置為2的時候,表示每次事務提交時都只是把redo log寫到page cache。
注意,如果把 innodb_flush_log_at_trx_commit 設置為 1,那么 redo log 在兩階段提交的第一個 prepare 階段就會刷盤,第二個 commit 階段只會 write page cache。
雙“1”配置
MySQL 的雙“1”配置指的就是 innodb_flush_log_at_trx_commit=1 和 sync_binlog=1 。即一次事務提交,需要等待兩次 fsync。
組提交(group commit)
這時候,你可能有一個疑問,這意味着我從MySQL看到的TPS是每秒兩萬的話,每秒就會寫四萬次磁盤。但是,我用工具測試出來,磁盤能力也就兩萬左右,怎么能實現兩萬的TPS?
解釋這個問題,就要用到組提交 group commit
機制了。
這里,我需要先和你介紹日志邏輯序列號 LSN
(log sequence number)的概念。LSN是單調遞增的,用來對應redo log的一個個寫入點。每次寫入長度為length的redo log, LSN的值就會加上length。
組提交舉例:
對於多個並發事務,他們都寫完了 redo log buffer,准備持久化到磁盤,那么會從這些事務中選擇一個 leader,然后取他們中最大的 LSN,讓這個 leader 帶着最大的 LSN 取寫盤,這樣小於 LSN 的日志就都寫到了磁盤,也就完成了一個組提交,其他事務直接返回即可。
所以,一次組提交中事務越多,可以節省的 IOPS 也就越多。
MySQL 在進行兩階段提交時,redo log 和 binlog 都是可以使用組提交的。此外,為了提高 binlog 使用組提交的效果,可以設置 binlog_group_commit_sync_delay
和 binlog_group_commit_sync_no_delay_count
來實現:
-
binlog_group_commit_sync_delay參數,表示延遲多少微秒后才調用fsync;
-
binlog_group_commit_sync_no_delay_count參數,表示累積多少次以后才調用fsync。
注意,如果 sync_binlog 設置為 0,那么 binlog_group_commit_sync_delay 會進行延遲,但不會 fsync。
MySQL的IO性能優化
綜合所述,可以通過以下方法進行優化:
-
將 sync_binlog 設置為大於 1 的(一般為 100 ~ 1000),這樣可能會丟失最近的 N 條日志
-
將 innodb_flush_log_at_trx_commit 設置為 2,主機斷電會導致丟失數據
-
設置 binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count參數,提升組優化效果,減少寫盤次數,但是會增加事務響應時間,也可能有丟失日志的風險
參考
- [1] MySQL是怎么保證數據不丟的