MYSQL 主從同步異常解決方案

作者 | Love DN 

出品 | 腳本之家(ID:jb51net)

MYSQL 主從同步異常解決方案

理論上講,mysql 的主從同步無法保證 100% 不出現異常,mysql 本身並不能保證主從的穩定性,當前較流行的 MHA 等 mysql 主從同步解決方案也是盡最大努力保證數據的一致性,出現異常還是要進行人工干預。對於 MYSQL 加快主從同步的方式,經歷了庫間併發 - 組提交 - WriteSet 等一系列減緩複製延遲的問題,但是很遺憾,主從複製做的在快,也解決不了中途出現異常的情況,本文基於 MYSQL 採用 binlog 和利用組提交方式的前提,探討在主從複製期間產生異常後,應如何解決,達到防止數據丟失,最終恢復主從關係的目的。


前提知識

爲了方便閱讀,我們定義兩臺服務器分別是 A,B,A 作爲 master,B 是 slaver。(主主原理一樣)。在設置主從時,爲了防止因配置導致的主從同步問題,最好將主從的參數設置保持一致。

  1. 主從同步流程

    B 中有兩個重要的線程,一個是 I/O 線程,負責同步 A 的 binlog 日誌文件,然後將日誌文件寫到磁盤,寫到磁盤的文件起了個名字,叫中繼日誌,也就是 relaylog;另一個是 SQL 線程,負責讀取 relaylog 中的 sql,在從節點重放 SQL,以達到主從同步的目的,當然這裏面有很多優化細節,例如組提交模式下的線程模型,如何調節 relaylog 參數,使得中繼日誌快速落地等,這不屬於本文討論的範圍。mysql 就是使用此種方式,將 AB 數據同步。

  2. binlog 日誌格式

    binlog 都很熟悉,這是 mysql 執行命令的底牌,不區分存儲引擎,就像流水賬一樣,將 mysql 引擎層執行的 sql,儘可能詳細的記錄在案。但是太詳細,就會使得 binlog 日誌過大,太少,則無法還原真實面貌。那記錄的方式就成了關鍵,MYSQL 爲我們提供了三種記錄方式,使用 binlog_format = row|statement|mixed,特定類型下還有一些細微的調節。

    通過以下命令可以明文顯示 binlog 的內容

    mysqlbinlog  -vv  --base64-output=DECODE-ROWS  mysql-bin.000001

    下面以tb_test(id, stu_id, name)這個表爲例子
    (1)statement: 這種方式最好理解,直接將sql語句記錄在binlog中,但是缺點很明顯,例如insertinto tb_test values(1, uuid(), 'nnn');這樣從在執行時,導致id主從不一致
    	# at 609
        #210922 14:01:36 server id 1  end_log_pos 733 CRC32 0x65647349  Query   thread_id=2     exec_time=0     error_code=0
        SETTIMESTAMP=1632290496/*!*/;
        insertinto tb_test values(1, uuid(), 'nnn')
        /*!*/;
        # at 733
        #210922 14:01:36 server id 1  end_log_pos 764 CRC32 0x0829c9d1  Xid = 21
        COMMIT/*!*/;
        
    (2)row:這是mysql默認的方式,不會出現statement的問題,單同樣有缺點,在update時,會將所有的列都輸出到binlog日誌中,導致 	binlog的增長量很快,例如現有表 tb_test(id, stu_id, name),在執行update tb_test setname= 'xxx'whereid=1;真正記錄binlog的是
    	# at 294
        #210922 14:11:33 server id 1  end_log_pos 353 CRC32 0x52746c43  Table_map: `test_db`.`tb_test` mapped to number 108
        # at 353
        #210922 14:11:33 server id 1  end_log_pos 481 CRC32 0x4e0adf53  Update_rows: table id 108 flags: STMT_END_F
        ### UPDATE `test_db`.`tb_test`
        ### WHERE
        ###   @1=1 /* INT meta=0 nullable=0 is_null=0 */
        ###   @2='28851e78-1b6b-11ec-a9d1-d8c497b8d38b' /* VARSTRING(233) meta=233 nullable=1 is_null=0 */
        ###   @3='xx' /* VARSTRING(22) meta=22 nullable=1 is_null=0 */
        ### SET
        ###   @1=1 /* INT meta=0 nullable=0 is_null=0 */
        ###   @2='28851e78-1b6b-11ec-a9d1-d8c497b8d38b' /* VARSTRING(233) meta=233 nullable=1 is_null=0 */
        ###   @3='xxdd' /* VARSTRING(22) meta=22 nullable=1 is_null=0 */
        # at 481
        #210922 14:11:33 server id 1  end_log_pos 512 CRC32 0x59324426  Xid = 6
        COMMIT/*!*/;
    	這樣在where和set後將所有字段都列出,當然也能精簡binlog的輸出,在ROW模式下,使用	 	binlog_row_image=minimal|full|noblob,其中full是默認的,如上,將鏡像前後都設置,所謂鏡像就是表在執行update前的數據情況,where記錄了鏡像前的數據(BI),set記錄鏡像後的數據(AI),binlog中對於insert只有AI,delete只有BI,而update包含BI和AI,使用minimal後,如下:
    	# at 294
        #210922 14:14:21 server id 1  end_log_pos 353 CRC32 0x029a51df  Table_map: `test_db`.`tb_test` mapped to number 108
        # at 353
        #210922 14:14:21 server id 1  end_log_pos 441 CRC32 0x6cc35569  Update_rows: table id 108 flags: STMT_END_F
        ### UPDATE `test_db`.`tb_test`
        ### WHERE
        ###   @1=1 /* INT meta=0 nullable=0 is_null=0 */
        ###   @2='3d3475ee-1b6c-11ec-9c36-d8c497b8d38b' /* VARSTRING(233) meta=233 nullable=1 is_null=0 */
        ###   @3='nnn' /* VARSTRING(22) meta=22 nullable=1 is_null=0 */
        ### SET
        ###   @3='xxdd' /* VARSTRING(22) meta=22 nullable=1 is_null=0 */
        # at 441
        #210922 14:14:21 server id 1  end_log_pos 472 CRC32 0x1d0bc509  Xid = 5
        COMMIT/*!*/;
        SET @@SESSION.GTID_NEXT= 'AUTOMATIC'/* added by mysqlbinlog *//*!*/;
        DELIMITER ;
        爲什麼使用了minimal,鏡像前仍然是全量數據,因爲mysql規定,如果表沒有主鍵,則鏡像前依然和full一樣,這裏給tb_test增加個主鍵,altertable tb_test add PRIMARY key(id);在執行update
        # at 797
        #210922 14:16:03 server id 1  end_log_pos 856 CRC32 0x49bd88c1  Table_map: `test_db`.`tb_test` mapped to number 109
        # at 856
        #210922 14:16:03 server id 1  end_log_pos 904 CRC32 0xde32ec90  Update_rows: table id 109 flags: STMT_END_F
        ### UPDATE `test_db`.`tb_test`
        ### WHERE
        ###   @1=1 /* INT meta=0 nullable=0 is_null=0 */
        ### SET
        ###   @3='xxdds' /* VARSTRING(22) meta=22 nullable=1 is_null=0 */
        # at 904
        #210922 14:16:03 server id 1  end_log_pos 935 CRC32 0xdacdff35  Xid = 9
        COMMIT/*!*/;
    (3)mixed:兩者的結合,一般的SQL語句以statement形式展示sql語句,當涉及uuid等函數時,使用row的形態;
    在使用row和mixed時,可能會問,真正執行的sql語句無法從binlog中展示,很幸運,mysql爲我們提供了binlog_rows_query_log_events參數,設置ON|1時,sql語句會以註釋的形式寫在binlog中
        
    生產建議使用binlog_format=row 配合 binlog_row_image=minimal
  3. binlog 日誌內容

-- binlog開頭用語
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
DELIMITER /*!*/;
# at 4
#210922 14:46:31 server id 1  end_log_pos 123 CRC32 0x97df49ac  Start: binlog v 4, server v 5.7.27-0kord0.16.04.1k2-log created 210922 14:46:31 at startup
# Warning: this binlog is either in use or was not closed properly.
ROLLBACK/*!*/;
# at 123
#210922 14:46:31 server id 1  end_log_pos 154 CRC32 0x85340b11  Previous-GTIDs
# [empty]

-- 創建數據庫
# at 154
#210922 14:48:11 server id 1  end_log_pos 219 CRC32 0x0ccb3436  Anonymous_GTID  last_committed=0        sequence_number=1       rbr_only=no
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 219
#210922 14:48:11 server id 1  end_log_pos 402 CRC32 0xdfce7c53  Query   thread_id=2     exec_time=0     error_code=0
SETTIMESTAMP=1632293291/*!*/;
SET @@session.pseudo_thread_id=2/*!*/;
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;
SET @@session.sql_mode=1436549152/*!*/;
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
/*!\C utf8 *//*!*/;
SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collation_server=8/*!*/;
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
CREATEDATABASE/*!32312 IF NOT EXISTS*/`my_db`/*!40100 DEFAULT CHARACTER SET utf8 */
/*!*/;

-- 創建表
# at 402
#210922 14:54:57 server id 1  end_log_pos 467 CRC32 0x063edb06  Anonymous_GTID  last_committed=1        sequence_number=2       rbr_only=no
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 467
#210922 14:54:57 server id 1  end_log_pos 637 CRC32 0xa22dd94a  Query   thread_id=2     exec_time=0     error_code=0
use`my_db`/*!*/;
SETTIMESTAMP=1632293697/*!*/;
createtable tb_test (idint,stu_id varchar(64),namevarchar(32)) ENGINE=INNODB
/*!*/;

-- insert表數據
# at 637
#210922 14:56:18 server id 1  end_log_pos 702 CRC32 0xa55b302a  Anonymous_GTID  last_committed=2        sequence_number=3       rbr_only=yes
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 702
#210922 14:56:18 server id 1  end_log_pos 782 CRC32 0x15ecbfb9  Query   thread_id=2     exec_time=0     error_code=0
SETTIMESTAMP=1632293778/*!*/;
BEGIN
/*!*/;
# at 782
#210922 14:56:18 server id 1  end_log_pos 846 CRC32 0xf0b67ceb  Table_map: `my_db`.`tb_test` mapped to number 112
# at 846
#210922 14:56:18 server id 1  end_log_pos 897 CRC32 0xce3a064c  Write_rows: table id 112 flags: STMT_END_F
### INSERT INTO `my_db`.`tb_test`
### SET
###   @1=1 /* INT meta=0 nullable=1 is_null=0 */
###   @2='1111' /* VARSTRING(192) meta=192 nullable=1 is_null=0 */
###   @3='nanan' /* VARSTRING(96) meta=96 nullable=1 is_null=0 */
# at 897
#210922 14:56:18 server id 1  end_log_pos 928 CRC32 0x1a1f19ed  Xid = 17
COMMIT/*!*/;

-- update表
# at 928
#210922 14:57:31 server id 1  end_log_pos 993 CRC32 0x638f8e2f  Anonymous_GTID  last_committed=3        sequence_number=4       rbr_only=yes
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 993
#210922 14:57:31 server id 1  end_log_pos 1073 CRC32 0x5234432b         Query   thread_id=2     exec_time=0     error_code=0
SETTIMESTAMP=1632293851/*!*/;
BEGIN
/*!*/;
# at 1073
#210922 14:57:31 server id 1  end_log_pos 1137 CRC32 0x499487e2         Table_map: `my_db`.`tb_test` mapped to number 112
# at 1137
#210922 14:57:31 server id 1  end_log_pos 1195 CRC32 0x521abeb5         Update_rows: table id 112 flags: STMT_END_F
### UPDATE `my_db`.`tb_test`
### WHERE
###   @1=1 /* INT meta=0 nullable=1 is_null=0 */
###   @2='1111' /* VARSTRING(192) meta=192 nullable=1 is_null=0 */
###   @3='nanan' /* VARSTRING(96) meta=96 nullable=1 is_null=0 */
### SET
###   @3='xxxx' /* VARSTRING(96) meta=96 nullable=1 is_null=0 */
# at 1195
#210922 14:57:31 server id 1  end_log_pos 1226 CRC32 0xb9a42b3a         Xid = 18
COMMIT/*!*/

-- delete
# at 1226
#210922 14:58:31 server id 1  end_log_pos 1291 CRC32 0x913de440         Anonymous_GTID  last_committed=4        sequence_number=5       rbr_only=yes
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 1291
#210922 14:58:31 server id 1  end_log_pos 1371 CRC32 0x29f9b016         Query   thread_id=2     exec_time=0     error_code=0
SETTIMESTAMP=1632293911/*!*/;
BEGIN
/*!*/;
# at 1371
#210922 14:58:31 server id 1  end_log_pos 1435 CRC32 0x008566a7         Table_map: `my_db`.`tb_test` mapped to number 112
# at 1435
#210922 14:58:31 server id 1  end_log_pos 1485 CRC32 0xb2abadc4         Delete_rows: table id 112 flags: STMT_END_F
### DELETE FROM `my_db`.`tb_test`
### WHERE
###   @1=1 /* INT meta=0 nullable=1 is_null=0 */
###   @2='1111' /* VARSTRING(192) meta=192 nullable=1 is_null=0 */
###   @3='xxxx' /* VARSTRING(96) meta=96 nullable=1 is_null=0 */
# at 1485
#210922 14:58:31 server id 1  end_log_pos 1516 CRC32 0xdaabfcd8         Xid = 19
COMMIT/*!*/;

-- binlog文件結尾用語
SET @@SESSION.GTID_NEXT= 'AUTOMATIC'/* added by mysqlbinlog *//*!*/;
DELIMITER ;
# End of log file
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;

由上面日誌可以發現兩個問題,

(1)所有的增刪改,都有一句 /!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED//!/;

這是什麼情況,將事物的隔離級別設置成了讀已提交,MYSQL 默認的不是 RR 嗎?網上找了下,沒找到原因,我的理解:mysql 本身事務開啓是從執行 sql 開始,而 binlog 的記錄是從 commit 後記錄,

(2)增刪改都會產生 5 個事件,每個事件都以 at 事件起點 開頭;

這裏以 update 爲例子,說明 binlog 的日誌內容

-- 事件起點,以at開頭,任何增刪改前面都會有兩個at,一個標識last_committed,sequence_number信息,一個是thread_id,exec_time信息
# at 928
-- 事件發生的時間 然後標識server id,是從哪臺mysql產生的事件,last_committed時提交事務,組提交的方式,兩次事務可能一樣
#210922 14:57:31 server id 1  end_log_pos 993 CRC32 0x638f8e2f  Anonymous_GTID  last_committed=3        sequence_number=4    rbr_only=yes
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
-- 第二次at,標識執行線程id,和事務執行時間,錯誤碼,0代表無錯誤,真正的事務開始時間TIMESTAMP,BEGIN
# at 993
#210922 14:57:31 server id 1  end_log_pos 1073 CRC32 0x5234432b         Query   thread_id=2     exec_time=0     error_code=0
SETTIMESTAMP=1632293851/*!*/;
BEGIN
/*!*/;
-- 表明操作的是tb_test表,此表的ID是112,這值並不是不變的,是表載入內存時臨時分配的數值
# at 1073
#210922 14:57:31 server id 1  end_log_pos 1137 CRC32 0x499487e2         Table_map: `my_db`.`tb_test` mapped to number 112
-- 表明事件類型,Update_rows/Delete_rows/Write_rows
# at 1137
#210922 14:57:31 server id 1  end_log_pos 1195 CRC32 0x521abeb5         Update_rows: table id 112 flags: STMT_END_F
### UPDATE `my_db`.`tb_test`
### WHERE
###   @1=1 /* INT meta=0 nullable=1 is_null=0 */
###   @2='1111' /* VARSTRING(192) meta=192 nullable=1 is_null=0 */
###   @3='nanan' /* VARSTRING(96) meta=96 nullable=1 is_null=0 */
### SET
###   @3='xxxx' /* VARSTRING(96) meta=96 nullable=1 is_null=0 */
-- 對應的Xid事務ID
# at 1195
#210922 14:57:31 server id 1  end_log_pos 1226 CRC32 0xb9a42b3a         Xid = 18
COMMIT/*!*/

任何一個事務都是以BEGIN,到COMMIT的過程,中間可能會涉及事務執行時間,事件ID,事件類型,表ID等。
增刪改的流程可以粗略的記爲:last_committed -> thread -> table -> event(含SQL) -> Xid,對應的
showbinlogeventsin'mysql-bin.000001';中的Event_type列,其他的語句從這五種類型中都能找到答案。
1. Anonymous_Gtid: 包含last_committed,sequence_number,rbr_only設置GTID_NEXT
2. Query:包含thread_id,exec_time,error_code
3. Table_map:表名和ID
4. Update_rows/Delete_rows/Write_rows
5. Xid
同步異常解決方案

由第 1 條主從同步流程,可以看出重點在於兩個線程,所以查看主從是否異常的時候,往往關注 show slave status\G 中

如上兩個線程的運行狀態,如果存在一個 No,則說明主從已經停止,異常已經產生。爲了方便後續查看同步情況,這裏先說明各字段的含義

Slave_IO_State: Waiting for master to send event
                  Master_Host: 1.1.1.1 					-- master服務器IP
                  Master_User: repl					-- 主從同步用戶
                  Master_Port: 3306						-- 主庫端口
                Connect_Retry: 60						-- 建立連接嘗試次數
              Master_Log_File: mysql-bin.000014  -- master的binlog日誌,在master上執行show master status查看
          Read_Master_Log_Pos: 33603949			 -- 中繼器讀到的master binlog的位置
               Relay_Log_File: relay-bin.000014	-- 從庫中繼器的日誌文件
                Relay_Log_Pos: 367						-- 中繼日誌讀到的位置
        Relay_Master_Log_File: mysql-bin.000014			-- 當前中繼讀到的master的binlog文件
             Slave_IO_Running: Yes	-- I/O線程狀態
            Slave_SQL_Running: Yes  -- SQL線程狀態
              Replicate_Do_DB: 
          Replicate_Ignore_DB: mysql,information_schema,performance_schema,sys -- 主從同步忽略的庫
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 33603949			-- SQL線程執行到的Relay_Master_Log_File文件的位置
              Relay_Log_Space: 741
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0			-- 主從數據同步間隔時間
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 2			-- master庫的serverId,在my.cnf中配置的,主從不能設置一樣
                  Master_UUID: ccc05185-0bb1-11ec-9019-6c92bf04787e
             Master_Info_File: mysql.slave_master_info	-- master的信息,這裏根據master_info_repository配置決定
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 
     Last_SQL_Error_Timestamp: 
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 
            Executed_Gtid_Set: 
                Auto_Position: 0
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version:

關於主從同步異常,我們會從一下幾個思路去思考

(1)根據業務,將不需要的異常編碼排除,例如

1032:記錄不存在,當更新或刪除一個條不存在的記錄時,我們可以忽略此異常
1050:數據表已經存在
還有一些個性化的 1062,等,都可以使用slave-skip-errors=1032,1050跳過這些異常,
注意slave-skip-errors=all千萬不要這麼設置,研發認爲這樣設置肯定不會出現主從異常,但是很遺憾,這樣設置有多重危害:
  1.可能導致主從數據不一致
  2.當I/O線程異常時,例如relaylog,binlog等刷盤時異常,高併發下突然斷電等情況,使得日誌文件損壞,這類異常僅僅通過跳過是不能解決的

(2)純 SQL 執行異常

這類異常最好處理,找到執行錯誤的 sql,只需要查看爲什麼錯誤,如果對數據一致性不影響,直接在從庫執行

爲了確保無問題,可以查看Relay_Master_Log_File文件的Exec_Master_Log_Pos位置,看下到底是什麼問題導致異常,然後執行
stopslave;
setglobal sql_slave_skip_counter=1;
startslave;
跳過此異常

(3)I/O 異常情況

這類異常比較麻煩,也是本文講解的重點,分爲兩種:一種是 master 的 binlog 日誌異常,二種是 slave 的 relaylog 異常。

我們要做的首先確定 Relay_Master_Log_File,這是 slave 已經同步到的 master 的 binlog 日誌,通過 mysqlbinlog 獲取該文件的內容,找到最後的 eng_log_pos,利用

第一個參數是同步的binlog文件,第二個是pos,第三個是等待時間(單位秒)
selectmaster_pos_wait('mysql-bin.000017',25138958,5);
如果指定的pos值已經同步,改select將返回0,如果沒有同步,將阻塞直到指定的時間,根據Exec_Master_Log_Pos的值可以判定同步的具體pos在哪,這樣便能找到下一次同步的pos,通過slave上執行 
stopslave;
changemasterto master_log_pos=xxx;
startslave;
便能恢復數據同步。

接下來以經典的 1236 異常來說明怎麼解決 I/O 的問題,1236 問題並不是一個異常,而是一類異常,這類異常又有區分,不同的問題不同對待。

1. Got fatal error 1236 from master when reading data from binary log: 'Client requested master to startreplicationfromposition > filesize'
這類異常最常見,觸發的時機是主庫在不斷的數據入庫,突然斷電或強制重啓,或強刪(kill -9)導致主庫binlog刷盤異常,從而使從庫I/O線程讀取到主庫不存在的position,導致主從數據不一致。解決辦法
從庫執行show slave status; 查看Exec_Master_Log_Pos和Read_Master_Log_Pos已經不一致,通過mysqlbinlog查看主庫的Relay_Master_Log_File文件,找到對應Exec_Master_Log_Pos相近的pos,這裏有兩種情況,一種是有相近的pos值,然後從庫通過執行select master_pos_wait方式,查看已經同步的pos值,找到未同步的,然後從庫執行 change master to master_log_pos=xxx;
另外一種情況就是Relay_Master_Log_File沒有相近的pos值,這時候一般是本binlog已經完成同步,將master_log_file指向下一個文件,pos值改成下個文件的最小值即可。所以在配置主從時,爲了減少此類事件的發生,設置sync_binlog=1最爲靠譜,當然這是以犧牲性能爲代價。
2. Got fatal error 1236 from master when reading data from binary log
這個時候需要注意,很有可能是主庫的binlog已經被刪除,從庫無法從主庫獲取指定文件,這個只能在前期將expire_logs_days設置的較大些,例如180天,即6個月。
3. 從庫的異常斷電,強制重啓,強刪導致的relaylog刷盤異常
這類異常和主庫binlog異常大同小異,爲了防止此類事件的發生,建議設置sync_relay_log=1,這個意思是在從庫讀到binlog後,relaylog寫入磁盤的頻率,默認是1000,也就是當出現1000個事物事件後,從庫纔會執行fdatasync()此係統調用進行刷盤,1000之內的只是將數據寫入文件緩存中依賴操作系統刷盤,但是設置的太小又會導致從庫的I/O瓶頸,一直在刷盤,降低效率,所有需要綜合考慮設置此值。
非主流的異常問題

myisam 引擎表損壞導致的同步異常

筆者遇到過因爲 myisam 表索引文件損壞,導致的異常,這類異常排查起來比較麻煩,解決確很容易,通過 repair 命令或者 myisamchk 工具將表文件修復,然後執行

stop slave;
set global sql_slave_skip_counter=1;
start slave;
修復完把本次異常跳過即可。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/6_1qVRN4ParTI3Aj_vyIyw