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 |
|