最近踩到一個說大不大,說小不小的坑,在此分享出來給各位同學。事情是這樣的,線上有2臺服務器,1主1從。A -> B,B服務器從A服務器同步數據。每天使用xtrabackup在B服務器上面進行全備。某天A服務器掛了,後來由於某種原因無法進入系統了,只有重裝了系統,那麼此時要恢復A服務器的步驟就是在A服務器部署mysql實例,從B服務器上面拿備份恢復到A,再根據POS點change到B服務器,讓A服務器從B服務器同步。此時是B -> A。相信熟悉MySQL的人都知道步驟是沒有問題的。
但在這過程中還是出問題了,在A服務器從新從B服務器同步完成以後,確認沒有延時以後,此時把A重新恢復成了原來的角色,也就是主庫,架構又變回了A -> B。恢復完成以後詢問開發說沒有異常。到第二天的時候有玩家反饋數據不正確。此時進行數據差異的查找。最後發現A的數據比B的數據少。在經過幾番查找以及回憶操作步驟以後,發現踩了大坑。那就是我們安裝mysql實例的時候,server-id是根據服務器ip地址的後2位生成的,比如ip地址是:192.168.5.6,那麼server-id就是56。
有同學會問了,和server-id有毛關係啊。大家仔細想想mysql的雙主是怎樣確定binlog是否需要應用的?沒錯,那就是server-id,如果server-id是自己的就不再應用binlog,那麼我踩的坑就是當A再次重新向B同步的時候,A的server-id還是老的,沒有修改,B服務器的binlog裏面記錄的server-id就是A服務器的server-id,最後導致有一部分binlog沒有應用。原理已經說明了,那麼接下來進行簡單的實驗就可以論證了。
環境:
自己搭建一個測試環境,簡單的1主1從。
我主庫的server-id是
mysql> show variables like '%server_id%';+---------------+-------+| Variable_name | Value |+---------------+-------+| server_id | 25152 |+---------------+-------+1 row in set (0.00 sec)
從庫的server-id是
mysql> show variables like '%server_id%';+---------------+-------+| Variable_name | Value |+---------------+-------+| server_id | 25250 |+---------------+-------+1 row in set (0.00 sec)
主庫建庫,建表,插入數據:
mysql> create database yayun; Query OK, 1 row affected (0.00 sec) mysql> create table yayun.tb1 ( id int, age int, name char(20), primary key(id) ); Query OK, 0 rows affected (0.07 sec) mysql> use yayunDatabase changed mysql> insert into tb1 (id,age,name)values(1,18,'aa'); Query OK, 1 row affected (0.00 sec) mysql> insert into tb1 (id,age,name)values(2,18,'bb'); Query OK, 1 row affected (0.00 sec) mysql> select * from tb1;+----+------+------+| id | age | name |+----+------+------+| 1 | 18 | aa || 2 | 18 | bb |+----+------+------+2 rows in set (0.00 sec) mysql>
從庫查詢:
mysql> select * from tb1;+----+------+------+| id | age | name |+----+------+------+| 1 | 18 | aa || 2 | 18 | bb |+----+------+------+2 rows in set (0.00 sec) mysql>
此時數據是一致的。
接下來在從庫備份數據,並且記錄pos點。(這裏模擬的是從庫每天進行的備份)
mysqldump -uroot -p --master-data=2 yayun > /tmp/backup_yayun.sql
下面在主庫繼續進行insert,update操作。
mysql> insert into tb1 (id,age,name)values(3,19,'cc'); Query OK, 1 row affected (0.00 sec) mysql> update tb1 set name='yayun' where id=1; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0mysql> select * from tb1;+----+------+-------+| id | age | name |+----+------+-------+| 1 | 18 | yayun || 2 | 18 | bb || 3 | 19 | cc |+----+------+-------+3 rows in set (0.00 sec) mysql>
查詢從庫記錄:
mysql> select * from tb1;+----+------+-------+| id | age | name |+----+------+-------+| 1 | 18 | yayun || 2 | 18 | bb || 3 | 19 | cc |+----+------+-------+3 rows in set (0.00 sec) mysql>
可以看到此時主從數據是一致的。接下來我們就當主庫掛了。重新需要拉取備份,然後向從庫同步數據。
1. 把備份文件backup_yayun.sql拉到主庫。
2. 把從庫的同步斷掉,清掉同步信息。
從庫操作:
mysql> stop slave;reset slave all; Query OK, 0 rows affected (0.03 sec) Query OK, 0 rows affected (0.05 sec) mysql>
主庫操作:
mysql -uroot -p yayun < backup_yayun.sql
查看pos點:
[root@mdw ~]# grep -i change backup_yayun.sql -- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000004', MASTER_LOG_POS=4070; [root@mdw ~]#
主庫change到原來的從庫
mysql> CHANGE MASTER TO MASTER_HOST='10.36.25.250',MASTER_USER='repl',MASTER_PASSWORD='123',MASTER_LOG_FILE='mysql-bin.000004',MASTER_LOG_POS=4070; Query OK, 0 rows affected (0.10 sec) mysql> start slave; Query OK, 0 rows affected (0.00 sec)
查詢數據:
如果查詢出來的數據是下面的數據,那麼就是正確的:
+----+------+-------+| id | age | name |+----+------+-------+| 1 | 18 | yayun || 2 | 18 | bb || 3 | 19 | cc |+----+------+-------+
我們實際查詢一下:
mysql> select * from tb1;+----+------+------+| id | age | name |+----+------+------+| 1 | 18 | aa || 2 | 18 | bb |+----+------+------+2 rows in set (0.00 sec) mysql>
臥槽,發生了什麼,怎麼數據少了,而且id等於1的name字段結果也不一樣?
下面我們看看原來老的從庫的binlog
#160908 13:45:57 server id 25152 end_log_pos 4239 Query thread_id=16 exec_time=0 error_code=0SET TIMESTAMP=1473313557/*!*/; insert into tb1 (id,age,name)values(3,19,'cc')/*!*/; # at 4239#160908 13:45:57 server id 25152 end_log_pos 4266 Xid = 160COMMIT/*!*/; # at 4266#160908 13:46:20 server id 25152 end_log_pos 4325 Query thread_id=16 exec_time=0 error_code=0SET TIMESTAMP=1473313580/*!*/; BEGIN/*!*/; # at 4325#160908 13:46:20 server id 25152 end_log_pos 4427 Query thread_id=16 exec_time=0 error_code=0SET TIMESTAMP=1473313580/*!*/; update tb1 set name='yayun' where id=1/*!*/; # at 4427#160908 13:46:20 server id 25152 end_log_pos 4454 Xid = 162COMMIT/*!*/; DELIMITER ;
可以看見有insert,update,但是server id都是25152,也就是主庫的。這也就是爲什麼少了數據的原因。開頭也提到過了。
如果我們在新的主庫上面進行update,如果這條記錄在從庫沒有存在,而且主從的binlog是row模式,那麼就會觸發1032錯誤,複製將中斷,由於我的是mixed模式,同步一直沒有報錯,沒有早發現問題。我update語句加limit就會觸發row模式,下面我們試試。
主庫:
mysql> update tb1 set name='abcd' where id=3 limit 1; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0mysql>
從庫:
mysql> show slave status\G*************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 10.36.25.250 Master_User: repl Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000004 Read_Master_Log_Pos: 4653 Relay_Log_File: relaylog.000002 Relay_Log_Pos: 253 Relay_Master_Log_File: mysql-bin.000004 Slave_IO_Running: Yes Slave_SQL_Running: No Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 1032 Last_Error: Could not execute Update_rows event on table yayun.tb1; Can't find record in 'tb1', Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND; the event's master log mysql-bin.000004, end_log_pos 4626 Skip_Counter: 0 Exec_Master_Log_Pos: 4454 Relay_Log_Space: 601 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: NULLMaster_SSL_Verify_Server_Cert: No Last_IO_Errno: 0 Last_IO_Error: Last_SQL_Errno: 1032 Last_SQL_Error: Could not execute Update_rows event on table yayun.tb1; Can't find record in 'tb1', Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND; the event's master log mysql-bin.000004, end_log_pos 4626 Replicate_Ignore_Server_Ids: Master_Server_Id: 252501 row in set (0.00 sec)
可以看見拋1032錯誤,主庫有這條記錄,從庫沒有,同時觸發了row模式,就會導致複製中斷。
結論:
1. 在重新搭建複製關係的時候一定注意server-id。
2. 線上對數據一致性要求比較高的一定要使用row模式。
作者:Atlas
出處:Atlas的博客 http://www.cnblogs.com/gomysql
您的支持是對博主最大的鼓勵,感謝您的認真閱讀。本文版權歸作者所有,歡迎轉載,但請保留該聲明。