MySQL 主從架構已經被普遍應用,保障主從複製關係的穩定性是你們一直關注的焦點。MySQL 5.6 針對主從複製穩定性提供了新特性: slave 支持 crash-safe。該功能能夠解決以前版本中系統異常斷電可能致使 relay_log.info 位點信息不許確的問題。
本文將從原理,參數,新的問題等幾個方面對該特性進行介紹。html
在瞭解 slave crash-safe 以前,咱們先分析 MySQL 5.6 以前的版本出現 slave crash-unsafe 的緣由。咱們知道在一套主從結構體系中,slave 包含兩個線程:即 IO thread 和 SQL thread。兩個線程的執行進度(偏移量)都保存在文件中。mysql
IO thread 負責從 master 拉取 binlog 文件並保存到本地的 relay-log 文件中。SQL thread 負責執行重複 sql,執行 relay-log 記錄的日誌。算法
crash-unsafe 狀況下 SQL_thread 的 的工做模式:sql
START TRANSACTION; Statement 1 ... Statement N COMMIT; Update replication info files (master.info, relay_log.info)
IO thread 的執行狀態信息保存在 master.info 文件, SQL thread 的執行狀態信息保存在 relay-log.info 文件。slave 運行正常的狀況下,記錄位點沒有問題。可是每當系統發生 crash,存儲的偏移量多是不許確的(須要注意的是這些文件被修改後不是同步寫入磁盤的)。由於應用 binlog 和更新位點信息到文件並非原子操做,而是兩個獨立的步驟。好比 SQL thread 已經應用 relay-log.01 的4個事務架構
trx1(pos:10) trx2(pos:20) trx3(pos:30) trx4(pos:40)
可是 SQL thread 更新位點 (relay-log.01,30) 到 relay-log.info 文件中,slave 實例重啓的時候 sql thread 會重複執行事務 trx4,因而乎,你們就看到比較常見的複製報錯 error 1062,error 1032。app
MySQL 5.5 經過兩個參數來緩解該問題,使用 sync_master_info=1 和sync_replay_log_info=1 來保證 Slave 的兩個線程每次寫一個事務就分別向兩個文件同步一次 IO thread 和 SQL thread 當前執行的位點信息。固然同步操做不是免費的,頻繁更新磁盤文件須要消耗性能。async
可是,即便設置了 sync_master_info=1 和 sync_relay_info=1,問題仍是會出現,由於複製信息是在 transactions 提交後寫入的,若是 crash 發生在事務提交和 OS 寫文件之間,那麼 relay-log.info 就多是錯誤的。當 slave 重新啓動的時候,最後那個事務可能會被執行兩次.具體的影響取決於事務的具體操做.複製可能會繼續運行好比 update/delete,或者報錯 好比 insert 操做,此時主從數據的一致性可能會被破壞。ide
經過上面的分析,咱們知道 slave crash-unsafe 的緣由在於應用 binlog 和更新文件的非原子性。MySQL 5.6 版本經過將更新位點信息存放到表中,而且和正常的事務一塊兒執行,進而保障 apply binlog 的事務和更新 relay info 信息到 slave_relay_log_info 的原子性.post
就是把 SQL thread 執行事務和更新 mysql.slave_replay_log_info 的語句合併爲同一個事務,由 MySQL 系統來保障事務的原子性。咱們能夠經過僞代碼來模擬 crash-safe 的原理:crash-safe 狀況下 SQL_thread 的工做模式性能
START TRANSACTION; Statement 1 ... Statement N Update replication info COMMIT
一圖勝千言:
綠色的表明實際業務的事務,藍色的是開啓 MySQL 執行的更新slave_replay_log_info 相關位點信息的 sql ,而後將這兩個 sql 合併在一個事務中執行,利用 MySQL 事務機制和 InnoDB 表保障原子性。不會出現應用 binlog 和更新位點信息兩個動做割裂致使不一致的問題。
經過設置 relay_log_recovery = ON,slave 遇到異常 crash,而後重啓的時候,系統會刪除現有的 relay log,而後 IO thread 會從 mysql.slave_replay_log_info 記錄的位點信息從新拉取主庫的 binlog。MySQL 如此設計的出發點是:
一圖勝千言:
藍色的 update 語句表明已經執行並提交的事務,綠色的 delete 語句表示正在執行的 sql,還未提交。此時 slave_replay_log_info 表記錄的 relay log info是**update 語句結束,delete 語句開始以前的位點
(relay_log.01,100)** 。若是遇到系統 crash,slave 實例重啓以後,會刪除已經有的 relaylog,而且 IO thread 會從(relay_log.01,100)對應的 master binlog 位點從新拉取主庫的 binlog,SQL thread 也會從這個位點開始應用 binlog。
和基於位點的複製不一樣,GTID 模式下使用新的複製協議 COM_BINLOG_DUMP_GTID 進行復制。舉個🌰
實例 a 的事務集合 set_a, 實例 b 的事務集合 set_b ,設置 b 爲 a 的從庫的時候,其中的 binlog 協議僞算法以下:
實例 b 將 GTID 信息發送給實例 a
UNION(@@global.gtid_executed, Retrieved_gtid_set - last_received_GTID)
a 若是不包含,表示實例 a 已經把實例 b 須要的 binlog 刪除了,直接返回報錯。
b 若是確認所有包含 實例 a 從本地 binlog 文件裏面,找到第一個不在 set_b 的事務,發送給實例 b。
GTID 模式下,slave crash-safe 運行機制
藍色 ABC:3 表示已經執行並提交的事務,綠色 ABC:4表示正在執行的事務,此時 slave crash,實例記錄的 gtid_executed=ABC:1-3,系統重啓 relay_log 被刪除。slave 將 UNION(@@global.gtid_executed, null) 發送到主庫,主庫會將 ABC:3 之後的 binlog 傳送給 slave 繼續執行。
注意
重新的複製協議中slave重啓時是基於binlog中的GTID信息進行復制的,並不依賴於mysql.slave_replay_log_info。爲了保障binlog及時落盤slave要設置 雙1模式 **sync_binlog = 1
innodb_flush_log_at_trx_commit = 1**
經過配置兩個以下兩個參數開啓該特性。
relay_log_info_repository = TABLE relay_log_recovery = ON
看到這裏是否是有疑問爲何沒有 master.info 相關的參數配置?
其實開啓 slave 的 crash-safe 以後,slave 重啓的時候會自動清空以前的 relay-log,IO thread 從 mysql.slave_relay_log_info 表中記錄的位點開始拉取數據,而不是依賴 slave_master_info 表相關數據。
注意:
若是是 MySQL 5.6.5 或者更早期。slave_master_info 和 slave_relay_log_info 表默認使用 MyISAM 引擎。因此還得修改爲 innodb,以下:
ALTER TABLE mysql.slave_master_info ENGINE=InnoDB;
ALTER TABLE mysql.slave_relay_log_info ENGINE=InnoDB;
開啓 crash-safe 以後,slave 重啓以後,再也不依賴 master info 相關的參數,因此這兩個參數不作過多討論。不過爲了和 relay log info 存儲一致,推薦存儲 maste-info 到表裏,sync_master_info 保持默認,設置爲比較低的值,在寫壓力比較大的狀況下,會有 IO 損耗。
master_info_repository =TABLE sync_master_info=0
開啓 crash-safe 必要參數
relay_log_info_repository = TABLE relay_log_recovery = 1
這 2 個很少作介紹了,前面已經將的很是透徹。
當 relay_log_info_repository=file 時,
更新位點信息的頻率依賴於sync_relay_log_info = N (N>=0):
a 當 sync_relay_log_info=0 時,MySQL 依賴 OS 系統按期更新。
b 當 sync_relay_log_info=N時(N>0),
MySQL server 會在每執行 N 個事務以後調用 fdatasync() 刷 relay-log.info 文件。
當 relay_log_info_repository=table
若是 mysql.slave_relay_log_info 是 innodb 存儲引擎,則每次事務更新,系統會自動忽略 sync_relay_log_info 的設置。
若是 mysql.slave_relay_log_info 是非事務存儲引擎,則
a 當 sync_relay_log_info=0 時,不更新。
b 當 sync_relay_log_info=N 時(N>0),
MySQL server 會在每執行 N 個事務以後調用 fdatasync() 刷 relay-log.info 文件。
sync_relay_log 控制着 relay-log 的刷新策略,相似 sync_binlog。不過這個參數在開啓 crash-safe 特性以後沒有什麼實質的意義。建議保持該參數爲默認值便可。
每一個硬幣都有它的兩面性。開啓 crash-safe 會帶來哪些潛在的問題?
1 重啓 slave,從新拉取 relay-log,一主多從的集羣會給主庫帶來 IO 和帶寬壓力。
2 主庫不可用,或者 binlog 被刪除了,slave 找不到所須要的 binlog。
[1] https://hackmongo.com/post/cr...
[2] http://dev.mysql.com/doc/refm...
[3] http://dev.mysql.com/doc/refm...