1. 程式人生 > >mysql主從同步常見錯誤以及解決方法總結

mysql主從同步常見錯誤以及解決方法總結

前言

在發生故障切換後,經常遇到的問題就是同步報錯,資料庫很小的時候,dump完再匯入很簡單就處理好了,但線上的資料庫都150G-200G,如果用單純的這種方法,成本太高,故經過一段時間的摸索,總結了幾種處理方法。

生產環境架構圖

目前現網的架構,儲存著兩份資料,通過非同步複製做的高可用叢集,兩臺機器提供對外服務。在發生故障時,切換到slave上,並將其變成master,壞掉的機器反向同步新的master,在處理故障時,遇到最多的就是主從報錯。下面是我收錄下來的報錯資訊。

常見錯誤

最常見的3種情況

這3種情況是在HA切換時,由於是非同步複製,且sync_binlog=0,會造成一小部分binlog沒接收完導致同步報錯。

第一種:在master上刪除一條記錄,而slave上找不到。

Last_SQL_Error: Could not execute Delete_rows event on table hcy.t1; 
Can't find record in 't1', 
Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND; 
the event's master log mysql-bin.000006, end_log_pos 254

第二種:主鍵重複。在slave已經有該記錄,又在master上插入了同一條記錄。

Last_SQL_Error: Could not execute Write_rows event on table hcy.t1; 
Duplicate entry '2' for key 'PRIMARY', 
Error_code: 1062; 
handler error HA_ERR_FOUND_DUPP_KEY; the event's master log mysql-bin.000006, end_log_pos 924

第三種:在master上更新一條記錄,而slave上找不到,丟失了資料。

Last_SQL_Error: Could not execute Update_rows event on table hcy.t1;
Can't find record in 't1', 
Error_code: 1032; 
handler error HA_ERR_KEY_NOT_FOUND; the event's master log mysql-bin.000010, end_log_pos 263

非同步半同步區別

非同步複製
簡單的說就是master把binlog傳送過去,不管slave是否接收完,也不管是否執行完,這一動作就結束了.

半同步複製
簡單的說就是master把binlog傳送過去,slave確認接收完,但不管它是否執行完,給master一個訊號我這邊收到了,這一動作就結束了。(谷歌寫的程式碼,5.5上正式應用。)

非同步的劣勢
當master上寫操作繁忙時,當前POS點例如是10,而slave上IO_THREAD執行緒接收過來的是3,此時master宕機,會造成相差7個點未傳送到slave上而資料丟失。

特殊的情況

slave的中繼日誌relay-bin損壞。
Last_SQL_Error: Error initializing relay log position: I/O error reading the header from the binary log
Last_SQL_Error: Error initializing relay log position: Binlog has bad magic number; 
It's not a binary log file that can be used by this version of MySQL

這種情況SLAVE在宕機,或者非法關機,例如電源故障、主機板燒了等,造成中繼日誌損壞,同步停掉。

人為失誤需謹慎:多臺slave存在重複server-id
這種情況同步會一直延時,永遠也同步不完,error錯誤日誌裡一直出現上面兩行資訊。解決方法就是把server-id改成不一致即可。

Slave: received end packet from server, apparent master shutdown:
Slave I/O thread: Failed reading log event, reconnecting to retry, log 'mysql-bin.000012' at postion 106

問題處理

刪除失敗

在master上刪除一條記錄,而slave上找不到。

Last_SQL_Error: Could not execute Delete_rows event on table hcy.t1; 
Can't find record in 't1',
Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND; 
the event's master log mysql-bin.000006, end_log_pos 254

解決方法:

由於master要刪除一條記錄,而slave上找不到故報錯,這種情況主上都將其刪除了,那麼從機可以直接跳過。可用命令:

stop slave;
set global sql_slave_skip_counter=1;
start slave;

如果這種情況很多,可用我寫的一個指令碼skip_error_replcation.sh,預設跳過10個錯誤(只針對這種情況才跳,其他情況輸出錯誤結果,等待處理),這個指令碼是參考maakit工具包的mk-slave-restart原理用shell寫的,功能上定義了一些自己的東西,不是無論什麼錯誤都一律跳過。)

主鍵重複

在slave已經有該記錄,又在master上插入了同一條記錄。

?
1 2 3 4 Last_SQL_Error: Could not execute Write_rows event on table hcy.t1; Duplicate entry '2' for key 'PRIMARY', Error_code: 1062; handler error HA_ERR_FOUND_DUPP_KEY; the event's master log mysql-bin.000006, end_log_pos 924

解決方法:

在slave上用desc hcy.t1; 先看下錶結構:

?
1 2 3 4 5 6 7 mysql> desc hcy.t1; +-------+---------+------+-----+---------+-------+ | Field | Type  | Null | Key | Default | Extra | +-------+---------+------+-----+---------+-------+ | id  | int(11) | NO  | PRI | 0    |    | | name | char(4) | YES |   | NULL  |    | +-------+---------+------+-----+---------+-------+

刪除重複的主鍵

?
1 2 3 4 5 6 7 8 9 10 11 12 mysql> delete from t1 where id=2; Query OK, 1 row affected (0.00 sec)   mysql> start slave; Query OK, 0 rows affected (0.00 sec)   mysql> show slave status\G; …… Slave_IO_Running: Yes Slave_SQL_Running: Yes …… mysql> select * from t1 where id=2;

在master上和slave上再分別確認一下。

更新丟失

在master上更新一條記錄,而slave上找不到,丟失了資料。

?
1 2 3 4 5 Last_SQL_Error: Could not execute Update_rows event on table hcy.t1; Can't find record in 't1', Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND; the event's master log mysql-bin.000010, end_log_pos 794

解決方法:

在master上,用mysqlbinlog 分析下出錯的binlog日誌在幹什麼。

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 /usr/local/mysql/bin/mysqlbinlog --no-defaults -v -v --base64-output=DECODE-ROWS mysql-bin.000010 | grep -A '10' 794   #120302 12:08:36 server id 22 end_log_pos 794 Update_rows: table id 33 flags: STMT_END_F ### UPDATE hcy.t1 ### WHERE ###  @1=2 /* INT meta=0 nullable=0 is_null=0 */ ###  @2='bbc' /* STRING(4) meta=65028 nullable=1 is_null=0 */ ### SET ###  @1=2 /* INT meta=0 nullable=0 is_null=0 */ ###  @2='BTV' /* STRING(4) meta=65028 nullable=1 is_null=0 */ # at 794 #120302 12:08:36 server id 22 end_log_pos 821 Xid = 60 COMMIT/*!*/; DELIMITER ; # End of log file ROLLBACK /* added by mysqlbinlog */; /*!50003 SET [email protected]_COMPLETION_TYPE*/;

在slave上,查詢下更新後的那條記錄,應該是不存在的。

mysql> select * from t1 where id=2;
Empty set (0.00 sec)

然後再到master檢視

?
1 2 3 4 5 6 7 mysql> select * from t1 where id=2; +----+------+ | id | name |