1. 程式人生 > >一條更新操作引起的MySQL主從複製異常

一條更新操作引起的MySQL主從複製異常

一、環境描述

生產環境異地機房主從資料庫,資料量過百G,資料庫版本社群版本5.6.25。

二、問題描述

同事根據開發提供的SQL在Master節點執行了一個大表的的全表更新操作,導致從節點Slave IO執行緒中斷。

三、問題分析

1)相關引數

my.cnf中有兩個引數設定:

expire_logs_days = 7        #binlog保留時間7天
max_binlog_size = 1G      #binlog大小

2)表大小,執行SQL

Table: v_clda   5.8G
Sql: update v_clda set uploadtime =now(); 主庫執行成功

3)主庫,大事物產生的binlog

-rw-rw—- 1 mysql mysql 1.1G Mar 16 02:49 mysql-bin.000159
-rw-rw—- 1 mysql mysql 8.0G Mar 16 15:28 mysql-bin.000160
-rw-rw—- 1 mysql mysql 7.4G Mar 16 18:13 mysql-bin.000161
-rw-rw—- 1 mysql mysql 1.1G Mar 16 23:55 mysql-bin.000162
-rw-rw—- 1 mysql mysql 1.1G Mar 17 12:15 mysql-bin.000163
-rw-rw—- 1 mysql mysql 1.1G Mar 18 16:54 mysql-bin.000164

4)異地從庫報錯

[ERROR] Slave I/O: Unexpected master’s heartbeat data: heartbeat is not compatible with local info;the event’s data:og_file_name mysql-bin.000160<90>Ó°Y log_pos 121238917, Error_code: 1623
[ERROR] Slave I/O: Relay log write failure: could not queue event from master, Error_code: 1595
[Note] Slave I/O thread exiting, read up to log ‘mysql-bin.000160’, position 3626103968
[Note] Error reading relay log event: slave SQL thread was killed

Slave 已經無法同步資料。

一個事物只能寫入一個binlog日誌中,預設情況下,binlog日誌達到設定值後(max_binlog_size),會自動生成一個新的日誌檔案,也會根據過期引數(expire_logs_days)設定自動刪除binlog日誌。如果生成了一個超大的binlog日誌,很可能是由於大事物引起的。

嘗試從啟slave執行緒,多次嘗試後失敗。

嘗試跳過事物,具體方法如下:

從節點執行(基於GTID)

stop slave;
SET @@SESSION.GTID_NEXT= ‘498815d6-20a9-11e6-a7d6-fa163e5770cc:53’; –根據實際情況
BEGIN; COMMIT;
SET SESSION GTID_NEXT = AUTOMATIC;
START SLAVE;
show slave status\G; 主從複製恢復正常

從節點執行更新操作,同步資料

set session sql_log_bin=0;
update v_clda set uploadtime =now();

在執行大事物前關閉 set session sql_log_bin=0; (預設是開啟的),尤其是異地機房,網路頻寬有限,而且VPN通道不是十分穩定的情況下。不允許它生成大量binlog日誌。

如果像本例中,已經執行了,而且生成了大量的binlog,最終導致複製異常,可以考慮使用跳過事物的方法來解決這個問題。

最笨的方法就是重新搭建主從,由於資料量比較大,還是異地不可取。

根本解決方法還是要拆分大事物,進行批量提交操作。賀春暘老師的MySQL管理之道一書中第四章4.4節有具體的解決方法。

參考改為用儲存過程,每刪除10000條事務就提交一次,迴圈操作直至刪除完畢。經過優化,行鎖的範圍變小了,效能也就變好了。相關程式碼如下:

DELIMITER   $$
USE   BIGDB$$
DROP   PROCEDURE IF EXISTS BIG_table_delete_10k$$
CREATE   PROCEDURE BIG_table_delete_10k(IN v_UserId INT)
BEGIN
del_10k:LOOP
delete   from BIGDB.BIGTABLE where UserId = v_UserId limit 10000;
select   row_count() into @count;
IF   @count = 0 THEN
select CONCAT(‘BIGDB.BIGTABLE UserId =   ‘,v_UserId,’ is ‘,@count,’ rows.’) as BIGTABLE_delete_finish;
LEAVE   del_10k;
END IF;
select   sleep(1);
END LOOP   del_10k;
END$$
DELIMITER   ;

作者:康壯 | 文章來源微信公眾號:DBAplus社群