MySQL crash-safe replication(1)
MySQL 5.6 對複製功能提供了新特性:slave 支援 crash-safe,可以解決之前版本中系統異常斷電可能導致的 SQL thread 資訊不準確的問題。
原文:Enabling crash-safe slaves with MySQL 5.6
可以對從庫進行配置 crash-safe 功能是 MySQL 5.6 關於複製的一個重大改進。然而,我們注意到對如何正確開啟這個特性存在著一些困惑,那麼讓我們一起來理清它要怎麼做。
簡而言之
1.停止從庫 MySQL 服務
2.在配置檔案 my.cnf 中新增 relay_log_info_repository = TABLE
relay_log_recovery = ON
3.重啟 MySQL 服務
詳情
如果要在從庫啟用 crash-safe 功能,你需要完全理解為什麼要做上面所說的配置。首先,讓我們看看當從庫崩潰時,同步會斷開的原因。
在一個從節點上,同步涉及到 2 個執行緒:把主節點的二進位制日誌( binary log )複製到本地中繼日誌( relay log )的 IO 執行緒,和執行中繼日誌中的語句的 SQL 執行緒。
每個執行緒當前的位置都儲存在一個檔案裡:IO 執行緒存在 master.info
檔案,SQL 執行緒存在 relay-log.info
檔案。
目前為止,還不錯。第一個問題是,這些檔案不是每次寫入都同步到磁碟:如果發生崩潰,寫入到檔案中的位置很可能是不準確的
set sync_master_info = 1
和 sync_relay_log_info = 1
來確保寫入兩個日誌檔案,且在每個事物完成之後同步到磁碟。同步到磁碟是有消耗的,但如果伺服器有回寫快取(write-back cache)策略,這些設定還是會起到積極作用,可以接受。
但是,等等,儘管設定了 set sync_master_info = 1
和 sync_relay_log_info = 1
,還是可能會出現問題。這是因為複製資訊是在事務提交後才寫到日誌檔案的。因此,如果在事務提交之後,複製資訊更新之前,發生了崩潰,當服務重啟的時候,複製資訊是錯的,並且一個事務會被執行兩次。這個影響取決於這些事務:複製可能還可以正常執行,或者斷開,或者導致資料不一致。
MySQL 5.6 通過讓我們把複製資訊儲存在表中,而不是之前的日誌檔案,來解決這個問題(當 relay_log_info_repository = TABLE
時,會建立表 mysql.slave_relay_log_info,當 master_info_repository = TABLE
時,會建立表 mysql.slave_master_info)。想法很簡單:我們可以把複製資訊的更新包含在事務當中,確保它和資料同步/一致。
虛擬碼,寫入到日誌檔案:
START TRANSACTION;
-- Statement 1
-- ...
-- Statement N COMMIT; -- Update replication info files
寫入到表:
START TRANSACTION;
-- Statement 1
-- ...
-- Statement N -- Update replication info COMMIT;
然而,這並沒有看起來那麼簡單。對於 SQL 執行緒而言,因為例項會在一個事務提交的同時更新表 slave_relay_log_info,所以它可以很好的工作。但對於 IO 執行緒而言,表的更新與事務的執行並沒有關係,那麼例項是如何知道什麼時候去更新這個表呢?
答案是:它由 sync_master_info
控制。預設值是 10000,表示 IO 執行緒的位置,只會每提交 10000 個事務更新一次。這明顯不利於從節點開啟 crash-safe 功能。一個辦法是,設定 sync_master_info = 1
,但正如前面所說,它可能會影響效能(這就是為什麼預設值不是 1)。
還有一個更加優雅的解決方法,那就是設定 relay_log_recovery = ON
,但它要求重啟 MySQL 服務。這個設定確保當 MySQL 服務啟動時,會從表 slave_relay_log_info
恢復出最新的 IO 執行緒的位置。因此,你甚至不需要為了從節點要開啟 crash-safe 功能而去把 IO 執行緒資訊儲存到表裡面。換句話說,沒有必要再去設定 master_info_repository = TABLE
。
最後再說一下,一旦設定了 relay_log_info_repository = TABLE
,因為這個表會在每個事物提交之後更新,所以 sync_relay_log_info
的設定是什麼就無關緊要了。因此,你可以安全地把它從配置檔案中刪除。