最近踩到一個說大不大,說小不小的坑,在此分享出來給各位同學。事情是這樣的,線上有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 yayun Database 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: 0 mysql> 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=0 SET 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 = 160 COMMIT/*!*/; # at 4266 #160908 13:46:20 server id 25152 end_log_pos 4325 Query thread_id=16 exec_time=0 error_code=0 SET 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=0 SET TIMESTAMP=1473313580/*!*/; update tb1 set name='yayun' where id=1 /*!*/; # at 4427 #160908 13:46:20 server id 25152 end_log_pos 4454 Xid = 162 COMMIT/*!*/; 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: 0 mysql>
從庫:
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: NULL Master_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: 25250 1 row in set (0.00 sec)
可以看見拋1032錯誤,主庫有這條記錄,從庫沒有,同時觸發了row模式,就會導致復制中斷。
結論:
1. 在重新搭建復制關系的時候一定注意server-id。
2. 線上對數據一致性要求比較高的一定要使用row模式。