type
status
date
slug
summary
tags
category
icon
password
設置建議
下圖是基於 MySQL 5.7.30~8.0.31 且 table engine = innodb:
若不考慮效能僅基於安全性有以下設置組合:
.png?table=block&id=1d58c3d6-7e80-80c8-a477-ef758343cc18&t=1d58c3d6-7e80-80c8-a477-ef758343cc18&width=4246&cache=v2)

考慮到效能進行簡化,推薦以下組合:
2x_(2).png?table=block&id=1d58c3d6-7e80-80e5-aa16-f3f2685effb9&t=1d58c3d6-7e80-80e5-aa16-f3f2685effb9&width=3222&cache=v2)
2x_(4).png?table=block&id=1d58c3d6-7e80-8097-bb18-db09734a862a&t=1d58c3d6-7e80-8097-bb18-db09734a862a&width=4220&cache=v2)
Master
必須是雙 1 的配置:
一來保證 Master 不丟失任何 Transaction,二來如果 sync_binlog ≠ 1 時有可能發生 Slave 資料多餘 Master 的狀況。
當 sync_binlog ≠ 1 時,會在 FLUSH 階段後就將 binlog 發送給 slave 應用,這導致了當 Master crash 丟失了 binlog 中部分事件在 recovery 時將其 rollback,這就導致 Slave 和 Master 不一致的狀況。
當 sync_binlog = 1 時,會在 SYNC 階段 (binlog fsync disk) 後才會將 binlog 發送給 slave 應用,當 Master crash 不會丟失 binlog 中的 Event,因此不會發生問題。
Slave
通用設定 ( < MySQL 8.0.23)
這 2 個參數 (
master_info_repository
和 relay_log_info_repository
) 用於設置 replication 相關的 metadata 要儲存為 file 還是 innodb table。這 2 個參數於 5.7 預設值為
file
,在 8.0~8.0.22 為 table
,從 8.0.23 開始棄用不需設置。備註:其實 master_info_repository 設置為 table 為非必要,因為只有 relay_log_info 會和 SQL thread 會在同一個 transaction 一起 commit。
未開啟 MTS ( slave_parallel_workers = 0 )
- 傳統 POSITION
- relay_log_recovery = 0
- relay_log_recovery = 1
說明:當 relay_log_recovery = 0 時,在 start slave 時 IO_thread 會從 connection metadata 中紀錄的
master_log_name
和 master_log_pos
開始繼續向 master 拉取 binlog event,而 SQL_thread 也會從 applier metadata 中的紀錄繼續讀取 relay log 進行回放。因此 relay log 和 connection metadata - mysql.slave_master_info
的完整性都非常重要,必須保證每個 event 寫入 relay log 時都必須 fsync relay log、mysql.slave_master_info
,因此設置了 sync_master_info = 1 及 sync_relay_log = 1 。注意:因為 sync_relay_log = 1 表示每個 event 寫入 relay log 都要 fsync 到 disk 一次,因此效能受到很大的影響。
備註: relay_log_info_repository = TABLE 時,因為 SQL_thread 的 applier metadata 也就是
mysql.slave_relay_log_info
換和回放的每個 event 處於同一個 transarction,也就是說會一起 commit、rollback,因此 sync_relay_log_info
的設置變得一點也不重要。說明:當 relay_log_recovery = 1 時,會在 start slave 時將 applier metadata 的
master_log_name
和 master_log_pos
覆蓋 connection metadata 的 master_log_name
和 master_log_pos
,也就是說 IO_thread 會重新從 SQL_thread 最後回放的 event 對應的 master binlog 位置重新向 master 拉取 binlog event ,而 SQL_thread 也不會使用舊有的 relay log 而是直接從新的 relay log 開始回放,也就是說 relay log 和 connection metadata 一點都不重要,因此保持預設值即可。- GTID AUTO_POSITION
- relay_log_recovery = 0
- Retrieved_Gtid_Set:slave IO_thread 已接收的 Gtid。
- Executed_Gtid_Set:slave 已經執行的 Gtid。
- relay_log_recovery = 1
- Retrieved_Gtid_Set:slave IO_thread 已接收的 Gtid。
- Executed_Gtid_Set:slave 已經執行的 Gtid。
說明:當 relay_log_recovery = 0 時,因為 SQL_thread 會從 applier metadata 中的紀錄繼續讀取 relay log 進行回放,因此 relay log 的完整性同樣重要,需要設置 sync_relay_log = 1。和傳統 position 的不同,開啟 Gtid 後有以下兩個變量:
其中
Retrieved_Gtid_Set
會從 relay log 中掃描確認 (同樣需設置 sync_relay_log = 1 ),並和 Executed_Gtid_Set
做並集 (UNION) 發送給 master,master 和自己的 Executed_Gtid_Set
比較將有落差的 event 發送給 slave,因此 connection metadata 不重要 sync_master_info 可以保預設值即可。說明:當 relay_log_recovery = 1 時,因為 SQL_thread 會被設定到新的 relay log (也就是不再使用舊有的 relay log),而且 Retrieved_Gtid_Set 不會初始化 ,因此 relay log 的完整性不重要 sync_relay_log 可以維持預設值。
和傳統 position 的不同,開啟 Gtid 後有以下兩個變量:
其中
Retrieved_Gtid_Set
不會初始化,因此和 Executed_Gtid_Set
做並集 (UNION) 的結果就還是 Executed_Gtid_Set
發送給 master,master 和自己的 Executed_Gtid_Set
比較將有落差的 event發送給 slave,因此 connection metadata 不重要 sync_master_info 可以保預設值即可。開啟 MTS ( slave_parallel_workers > 0 僅考慮 slave_parallel_type = LOGICAL_CLOCK )
- 傳統 POSITION
- relay_log_recovery = 0
- relay_log_recovery = 1
- slave_preserve_commit_order = OFF
- slave_preserve_commit_order = ON
說明:和未開啟 MTS 傳統 POSITION 相同,slave_preserve_commit_order 的開啟與否不影響 replication crash safe,只影響因果一致性。
說明:當開啟 MTS 且 slave_preserve_commit_order = OFF 時,由於會發生 GAP 因此在 slave 開啟時會從舊有的 relay log 中恢復 GAP,因此和未開啟 MTS 時不同,這邊必須保證 relay_log 完整性,也就是 sync_relay_log = 1 ,但也因此失去效能。
補充 - GAP:當在 Master 依序有 A → B → C 3個 Transaction 且不衝突,那在 Slave 設定 slave_preserve_commit_order = OFF 時有可能發生 B、C 已經 commit ,但是 A 可能某些原因還沒 commit,這時候就產生了 GAP。
說明:當開啟 MTS 且 slave_preserve_commit_order = ON 時,不會產生 GAP 因此 relay log 不需保證完整性,因此 sync_relay_log 可以保持預設值增加效能。
注意:在 MySQL 8.0.28 之前有此 BUG: MySQL Bugs: #103636: Slave hangs with slave_preserve_commit_order On ,當開啟 slave_preserve_commit_order = ON 在高負載長時間運行時會用盡 commit order sequence tickets 導致 applier 掛起 (hang) 並且無期限的持續等待 commit order queue。
- GTID AUTO_POSITION
- relay_log_recovery = 0
- Retrieved_Gtid_Set:slave IO_thread 已接收的 Gtid。
- Executed_Gtid_Set:slave 已經執行的 Gtid。
- relay_log_recovery = 1
設定和未開啟 MTS 時相同
說明:當 relay_log_recovery = 0 時,因為 SQL_thread 會從 applier metadata 中的紀錄繼續讀取 relay log 進行回放,因此 relay log 的完整性同樣重要,需要設置 sync_relay_log = 1。和傳統 position 的不同,開啟 Gtid 後有以下兩個變量:
其中
Retrieved_Gtid_Set
會從 relay log 中掃描確認 (同樣需設置 sync_relay_log = 1 ),並和 Executed_Gtid_Set
做並集 (UNION) 發送給 master,master 和自己的 Executed_Gtid_Set
比較將有落差的 event 發送給 slave,因此 connection metadata 不重要 sync_master_info 可以保預設值即可。唯一特別的是 slave_preserve_commit_order 不論 ON 還是 OFF 都不影響,因為 Gtid AUTO_POSITION 下會自行填補 GAP。
說明:slave 發送 Executed_Gtid_Set 給 master,master 和自己的
Executed_Gtid_Set
比較將有落差的 event 發送給 slave,同時這也會彌補 slave MTS 下的 GAP。例外情況 (請務必確認此段敘述)
- 雖然最推薦使用 relay_log_recovery 來減少對於 relay log 的依賴性 (也就是允許 relay log 損壞),但因為每次重啟 MySQL (不論是否 crash) 都會丟棄原有的 relay log,所以可能有以下問題:
- 如果 slave 未執行的 relay log 數量龐大,則當重啟 slave 時會影響 master 的網路相關 loading。
- 假設 master 不可用或 purge 掉 binlog,儘管 relay log 未損壞,但 relay_log_recovery 的關係 slave 因為無法獲取 binlog 無法同步。
社區建議官方只在 relay log 損壞時,才執行 relay_log_recovery
其他方案:可以使用 binlog server
- 在 MySQL 8.0.17 之前, GTID AUTO_POSITION 下如果 slave 有開啟
log_slave_update
則必須同時設定 sync_binlog = 1 & innodb_flush_log_at_trx_commit = 1,具體說明請參考 BUG#92109
- 在 MySQL 8.0.28 之前
slave_preserve_commit_order = ON
有 BUG,可能會用完 commit order sequence tickets 導致 applier 掛起 (hang) 並且無期限的持續等待 commit order queue。
- 傳統 POSITION 在開啟 MTS 且 slave_parallel_type = DATABASE 時,則必須設置 sync_relay_log = 1,因為沒有類似 slave_preserve_commit_order 的參數保證沒有 GAP 產生。
- 在 MySQL 8.0.19 之前 slave_preserve_commit_order = ON 必須要同時設置 log_slave_update,則無疑會進一步降低 replication 效率,因此更加不建議使用。
參數解說
- master_info_repository
- FILE:保存到
master.info
檔案中,這表示沒有事務性。 - TABLE:保存到 InnoDB TABLE,這表示有事務性。
該設定用來決定 connection metadata (也就是 IO thread 相關資訊) 如何保存。
當有多個 replication channel 時,此參數必須為 TABLE。
此參數在 5.7 預設為 FILE,在 8.0 預設為 TABLE,並在 8.0.23 棄用。
- sync_master_info、sync_source_info
- 當 master_info_repository = TABLE 時,表示每隔 N 個 event 之後更新
mysql.slave_master_info
。 - 當 master_info_repository = FILE 時,表示每隔 N 個 event 之後寫入
master.info
檔案並調用 fsync() 持久化到 disk。 - 當此參數設為 0 時,表示永遠不會更新 connection metadata。
該參數用來指定每隔多少 Event 之後更新 connection metadata (也就是 IO thread metadata)。
此參數預設值為 10000。
- sync_relay_log
- 當 sync_relay_log = 0,表示由 OS 控制決定何時將 relay log fsync() 到 disk 持久化。
- 當 sync_relay_log = N > 0,表示每當寫入 N 個 event 到 relay log 之後會將 relay log fsync() 到 disk 持久化。
此參數用來控制 relay log fsync() 到 disk 的策略。
此參數預設值為 10000。
- relay_log_info_repository
- FILE:保存到
relay-log.info
檔案中,這表示沒有事務性。 - TABLE:保存到 InnoDB TABLE,並且會和 SQL thread 應用的 Event 在同一個 Transaction 中,也就是說會一起 commit 或 rollback。
- 當未開啟 MTS 時,每次 Transaction commit 時都會更新
mysql.slave_relay_log_info
。 - 當開啟 MTS 時,每次 Transaction commit 時都會更新
mysql.slave_worker_info
,另外的mysql.slave_relay_log_info
由 MTS checkpoint 負責。
該設定用來決定 applier metadata (也就是 SQL thread 相關資訊) 如何保存。
當有多個 replication channel 時,此參數必須為 TABLE。
此參數在 5.7 預設為 FILE,在 8.0 預設為 TABLE,並在 8.0.23 棄用。
- sync_relay_log_info
- 當 relay_log_info_repository = TABLE 時,此參數的效果被忽略,因為
mysql.slave_relay_log_info
、mysql.slave_worker_info
會跟隨 transaction 一起 commit - 當 relay_log_info_repository = FILE 時:
- 當 sync_relay_log_info = 0,表示由 OS 控制決定何時將
relay-log.info
fsync() 到 disk 持久化。 - 當 sync_relay_log_info = N > 0,表示每 N 個 transaction 將
relay-log.info
fsync() 到 disk 持久化。
此參數根據 relay_log_info_repository 設置的不同有不同的效果:
此參數預設值為 10000。
- relay_log_recovery
- 創建一個新的 relay log
- 將 SQL thread 要執行的位置初始化到新的 relay log
- 將 IO thread 要開始讀取 Master 的 binlog 位置切換到 SQL thread 最後應用的位置
mysql.slave_relay_log_info
中的master_log_name
和master_log_pos
:表示 SQL thread 最後一個已經應用的 Event 在 Master binlog 對應的位置。mysql.slave_master_info
的master_log_name
和master_log_pos
:表示 IO thread 最後一個從 Master 讀取的 Event 對應在 Master binlog 對應的位置。- 當為 GTID AUTO_POSITION 模式下不受此參數影響,而是會透過 Retrieved_Gtid_Set 和 Executed_Gtid_Set 來判斷達成相似的效果。
- 對於有開啟 MTS 時,會自動計算並處理 GAP
- 若為 GTID 則不需要恢復,因為 GTID 本身會自行填充 GAP。
- 若為傳統 position mode 下則需要恢復, 恢復過程和使用 START SLAVE UNTIL SQL_AFTER_MTS_GAPS 語句相同。
當開啟 relay_log_recovery 時,slave 在啟動時會有以下行為:
也就是將記錄在
mysql.slave_relay_log_info
中的 relay_log_name 和 relay_log_pos 換成新的 relay log 和位置。也就是將記錄在
mysql.slave_relay_log_info
中的 master_log_name
和 master_log_pos
覆蓋 mysql.slave_master_info
的 master_log_name
和 master_log_pos
的值。也就是說會放棄原本的 relay log,從 SQL thread 最後應用的 Event 開始重新從 Master 拉取 binlog 下來開始回放。
另外還有以下特殊狀況:
此外從 8.0.18 開始 GTID AUTO_POSITION 模式下則省略計算 GAP 的流程。
當處理完 GAP 後才會接續上述提到的 recovery 過程。
相關 BUG 和 ISSUE
- MySQL Bugs: #70669: Slave can't continue replication after master's crash recovery
修復版本:5.6.17、5.7.4 修復
- MySQL Bugs: #77496: Replication position lost after crash on MTS configured slave
說明:當開啟 MTS 且 relay_log_recovery = 1,因為不會自動填補 GAP 導致無法自動恢復 replication,在修復前需要先將 relay_log_recovery 設為 0 並使用 START SLAVE UNTIL SQL_AFTER_MTS_GAPS 啟動 replication 填充 GAP,完成後再設回 relay_log_recovery = 1 後重啟。
修復版本:5.6.31、5.7.13
- MySQL Bugs: #81840: Automatic Replication Recovery Does Not Handle Lost Relay Log Events
- sync_relay_log = 1,保證 relay_log 完整性因此能修復 GAP
- slave_preserve_commit_order = ON ( 前提是
slave_parallel_mode = LOGICAL_CLOCK
),保證沒有 GAP 發生就不需要 relay log 完整性。 - 使用 GTID AUTO POSITION
說明:當開啟 MTS 且 relay_log_recovery = 1,當 sync_relay_log ≠ 1 時可能發生 relay log 損壞,當有 GAP 時會無法恢復。
修復版本:無
臨時方案:
- MySQL Bugs: #82667: Please clarify resiliency introduced by master_info_repository to TABLE.
說明:文檔中說明
master_info_repository = TABLE
能提高 replication crash safte,但實際上 master_info_repository = TABLE
對於 replication crash safe 沒有任何幫助。修復版本:文檔已修復
- MySQL Bugs: #92064: Information for the recovery of the IO thread is NOT in mysql.slave_master_info.
說明:文檔中說明當 relay_log_recovery = 1 時,IO thread 在 crash recovery 中會從 mysql.slave_master_info 中取得 master_log_name 和 master_log_position,但是實際上是從 mysql.slave_relay_log_info 中取得。
修復版本:文檔已修復
- MySQL Bugs: #92093: Replication crash safety needs relay_log_recovery even with GTID.
說明:GTID AUTO POSITION 時只要開啟 relay_log_recovery = 1 能保證 replication crash safe。
修復版本:文檔已修復
- MySQL Bugs: #92109: Please make replication crash safe with GITD and less durable setting (bis).
- 如果 slave 的 binlog 中的 transaction 多於 redolog,因為 mysql.gtid_executed 從 binlog 中判斷已經執行過對應的 Transaction,這會導致 InnoDB 丟失這些 Transaction。
- 如果 slave 的 binlog 中的 transaction 少於 redolog,因為 mysql.gtid_executed 從 binlog 中判斷尚未執行過對應的 Transaction,這會導致發生 duplicate key。
- 當
log_bin = OFF
或者log_slave_update = OFF
時,則 transaction 和 mysql.gtid_executed 會一起 commit 或 rollback。 - 當 slave
log_bin = ON
且log_slave_update = ON
時 (注意:如果是 masterlog_bin = ON
也適用), 每當 binlog rotate 或者 server 關閉時,才會將先前 binlog 的所有 transaction gtid 寫入 mysql.gtid_executed 表。
說明:當 slave 使用 gtid 且為 less durable 設定 (sync_binlog != 1 and innodb_flush_log_at_trx_commit != 1) 且 log_slave_update = ON 時,可能在 crash recovery 後發生以下問題-
在 MySQL 8.0.17 之前,Slave 會因為設定不同會影響 mysql.gtid_execute 更新的時機:
如果是 server crash 會在 crash recovery 的時候從 binlog 來恢復 mysql.gtid_executed 表。
注意:如果在 server crash 之後設定
log_bin = OFF
則會無法恢復 mysql.gtid_executed,且無法進行 replication。當不使用 gtid 時,因為是透過 mysql.slave_relay_log 繼續應用而不是 mysql.gtid_executed,加上 mysql.slave_relay_log 會隨著 transaction 一起更新,因此不會發生問題。
修復版本:8.0.17 (未移植到 5.7)
修復說明:MySQL 8.0.17 之後統一為 mysql.gtid_executed 和對應的 transaction 會一起更新,因此 gtid auto_posistion 的 slave 不在需要雙1。
相關 bug report:
- MySQL Bugs: #92882: MTS not replication crash-safe with GTID and all the right parameters.
說明:當開啟 MTS 在 GTID AUTO_POSITION 模式下且 relay_log_info_repository = TABLE、relay_log_recovery = ON 時,當發生 OS crash 導致 relay log 損壞導致恢復 gap 失敗,從而無法保證 replication crash-safe
修復版本:5.7.28、8.0.18 修復
修復說明: GTID AUTO_POSITION 不需要修復 GAP,因此跳過此步驟。
- MySQL Bugs: #101876: The manual is overly conservative in parameters for replication crash safety.
- 必要參數:
- relay_log_info_repository = TABLE
- relay_log_recovery = ON
- 其他狀況:
- MASTER_AUTO_POSITION = 1
- sync_relay_log = 1
- innodb_flush_log_at_trx_commit = 1 and sync_binlog = 1
說明:認為 MySQL 文檔中對於 replication crash safe 說明的設定過於保守
當 gtid_mode = ON & 開啟 MTS (slave_parallel_workers > 1) & out-of-order-commit (slave_parallel_type = DATABASE, or when slave_parallel_type = LOGICAL_CLOCK and slave_preserve_commit_order = OFF)
當 gtid_mode = OFF & 開啟 MTS (slave_parallel_workers > 1) & out-of-order-commit (slave_parallel_type = DATABASE, or when slave_parallel_type = LOGICAL_CLOCK and slave_preserve_commit_order = OFF)
當 gtid_mode = ON & log_slave_upates = ON (MySQL 8.0.17 之前)
修復版本:無
參考
- 官方文檔
- 書籍 - 深入理解 MySQL 主從原理 - 高鵬著 (網名:八怪)
演講 - Jean-François Gagné 在 percona live 2018、HighLoad++ 2018 ……的演講 Demystifying MySQL Replication Crash Safety
- Jean-François Gagné - MySQL Bugs: #93081: Please implement a better relay log recovery.
- Jean-François Gagné 對於官方文檔的 slave crash safe 建議