MySQL45講之IO效能提升
前言
本文介紹 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是怎麼保證資料不丟的