使用備份恢復實例時存在的坑

愛可生服務團隊成員,負責處理客戶在MySQL日常運維中遇到的問題;擅長處理備份相關的問題,對數據庫相關技術有濃厚的興趣,喜歡鑽研各種問題。


前言

在日常數據庫運維中,備份是不可缺少的一部分。我們常常用備份集來新建從庫或恢復數據不一致的實例等等。但有些時候恢復完實例加回集群后,是有可能會丟失數據的。


實驗

環境準備


技術分享 | 使用備份恢復實例時存在的坑


步驟

1、主從同步驗證

先在主庫寫入一些數據,然後驗證數據已經同步到從庫

<code>-- master(131)mysql> create database test1;Query OK, 1 row affected (0.00 sec)mysql> create database test2;Query OK, 1 row affected (0.01 sec)mysql> show databases;+--------------------+| Database           |+--------------------+| information_schema || mysql              || performance_schema || sys                || test1              || test2              |+--------------------+6 rows in set (0.00 sec)mysql> show master status;+------------------+----------+--------------+------------------+------------------------------------------+| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                        |+------------------+----------+--------------+------------------+------------------------------------------+| mysql-bin.000001 |      478 |              |                  | eefac7d8-2370-11e9-bfeb-000c29d74445:1-2 |+------------------+----------+--------------+------------------+------------------------------------------+1 row in set (0.00 sec)-- slave(132)mysql> show databases;+--------------------+| Database           |+--------------------+| information_schema || mysql              || performance_schema || sys                || test1              || test2              |+--------------------+6 rows in set (0.00 sec)mysql> show slave status\\G;*************************** 1. row ***************************               Slave_IO_State: Waiting for master to send event                  Master_Host: 192.168.13.131                  Master_User: repl                  Master_Port: 3306                Connect_Retry: 60              Master_Log_File: mysql-bin.000001          Read_Master_Log_Pos: 478               Relay_Log_File: 192-168-13-132-relay-bin.000002                Relay_Log_Pos: 691        Relay_Master_Log_File: mysql-bin.000001             Slave_IO_Running: Yes            Slave_SQL_Running: Yes            ......           Retrieved_Gtid_Set: eefac7d8-2370-11e9-bfeb-000c29d74445:1-2            Executed_Gtid_Set: eefac7d8-2370-11e9-bfeb-000c29d74445:1-2/<code>

2、 在從庫上創建備份

邏輯備份或物理備份都可以,這裡以邏輯備份為例

<code>mysqldump -uroot -p123456 --single-transaction --master-data=2 --all-databases > dump.sql/<code>

3、向主庫寫入數據

<code>-- master(131)mysql> create database test3;Query OK, 1 row affected (0.00 sec)mysql> show databases;+--------------------+| Database           |+--------------------+| information_schema || mysql              || performance_schema || sys                || test1              || test2              || test3              |+--------------------+7 rows in set (0.01 sec)mysql> show master status;+------------------+----------+--------------+------------------+------------------------------------------+| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                        |+------------------+----------+--------------+------------------+------------------------------------------+| mysql-bin.000001 |      640 |              |                  | eefac7d8-2370-11e9-bfeb-000c29d74445:1-3 |+------------------+----------+--------------+------------------+------------------------------------------+1 row in set (0.00 sec)-- slave(132)mysql> show databases;+--------------------+| Database           |+--------------------+| information_schema || mysql              || performance_schema || sys                || test1              || test2              || test3              |+--------------------+7 rows in set (0.00 sec)mysql> show slave status\\G;*************************** 1. row ***************************               Slave_IO_State: Waiting for master to send event                  Master_Host: 192.168.13.131                  Master_User: repl                  Master_Port: 3306                Connect_Retry: 60              Master_Log_File: mysql-bin.000001          Read_Master_Log_Pos: 640               Relay_Log_File: 192-168-13-132-relay-bin.000002                Relay_Log_Pos: 853        Relay_Master_Log_File: mysql-bin.000001             Slave_IO_Running: Yes            Slave_SQL_Running: Yes            ......           Retrieved_Gtid_Set: eefac7d8-2370-11e9-bfeb-000c29d74445:1-3            Executed_Gtid_Set: eefac7d8-2370-11e9-bfeb-000c29d74445:1-3/<code>

4、產生故障

假設現在主庫存放數據的磁盤發生故障,HA 將原來的從節點提升為主節點來提供數據的寫入,然後插入數據

<code>-- new master(132)mysql> create database test4;Query OK, 1 row affected (0.01 sec)mysql> show databases;+--------------------+| Database           |+--------------------+| information_schema || mysql              || performance_schema || sys                || test1              || test2              || test3              || test4              |+--------------------+8 rows in set (0.00 sec)mysql> show master status;+------------------+----------+--------------+------------------+----------------------------------------------------------------------------------+| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                                                                |+------------------+----------+--------------+------------------+----------------------------------------------------------------------------------+| mysql-bin.000001 |      802 |              |                  | b66b4623-207d-11ea-a993-000c29122c12:1,eefac7d8-2370-11e9-bfeb-000c29d74445:1-3 |+------------------+----------+--------------+------------------+----------------------------------------------------------------------------------+1 row in set (0.00 sec)/<code>

5、恢復實例

用之前備份的數據來恢復掛掉的實例

<code>-- old master(131)mysql -uroot -p123456 < dump.sql/<code>

恢復後擁有的數據

<code>mysql> show databases;+--------------------+| Database           |+--------------------+| information_schema || mysql              || performance_schema || sys                || test1              || test2              |+--------------------+6 rows in set (0.00 sec)mysql> show master status;+------------------+----------+--------------+------------------+------------------------------------------+| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                        |+------------------+----------+--------------+------------------+------------------------------------------+| mysql-bin.000001 |      478 |              |                  | eefac7d8-2370-11e9-bfeb-000c29d74445:1-2 |+------------------+----------+--------------+------------------+------------------------------------------+1 row in set (0.00 sec)/<code>

6、與新主建立複製關係

<code>-- old master(131)mysql> change master to    -> master_host='192.168.13.132',    -> master_user='repl',    -> master_password='123456',    -> master_auto_position=1;Query OK, 0 rows affected, 2 warnings (0.01 sec)mysql> start slave;Query OK, 0 rows affected (0.00 sec)mysql> show databases;+--------------------+| Database           |+--------------------+| information_schema || mysql              || performance_schema || sys                || test1              || test2              || test4              |+--------------------+7 rows in set (0.00 sec)mysql> show master status;+------------------+----------+--------------+------------------+----------------------------------------------------------------------------------+| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                                                                |+------------------+----------+--------------+------------------+----------------------------------------------------------------------------------+| mysql-bin.000001 |      640 |              |                  | b66b4623-207d-11ea-a993-000c29122c12:1,eefac7d8-2370-11e9-bfeb-000c29d74445:1-2 |+------------------+----------+--------------+------------------+----------------------------------------------------------------------------------+1 row in set (0.00 sec)/<code>


現象

可以觀察到,在舊主通過備份恢復數據且加回到集群后,丟失了部分的數據。操作的步驟看起來並沒有問題,那麼原因是什麼呢?

丟失的事務的 GTID 是 eefac7d8-2370-11e9-bfeb-000c29d74445:3,我們先到新主的 binlog 中看下是否還有這個事務的記錄,保證 binlog 沒有被清理。

<code>#200220 14:19:41 server id 1  end_log_pos 543 CRC32 0x3fa9fb8c  GTID    last_committed=2        sequence_number=3       rbr_only=noSET @@SESSION.GTID_NEXT= 'eefac7d8-2370-11e9-bfeb-000c29d74445:3'/*!*/;# at 543#200220 14:19:41 server id 1  end_log_pos 640 CRC32 0x8427b0a3  Query   thread_id=4     exec_time=0     error_code=0SET TIMESTAMP=1582179581/*!*/;create database test3/*!*/;/<code>

新主的 binlog 中是有這個事務的,那接著我們再來看下舊主的 relaylog 中是否也同樣記錄。但意外的是,relaylog 中並沒有相關的記錄,說明這個事務根本沒有寫進 relaylog。

那麼我們可以回想一下,在 MySQL 裡面,有沒有類似的機制或者參數讓主庫的 binlog 在傳輸或寫入從庫的過程中被過濾掉。正好我們發現,開啟 binlog 必須要配置的 server_id 就能起到這樣的作用。


分析

由於我們在恢復實例的時候使用的備份集不是最新的備份集,這個備份集中只含有 eefac7d8-2370-11e9-bfeb-000c29d74445:1-2 兩個事務,那麼恢復回來的實例加入集群變成從實例的時候,需要通過新主庫的 binlog 來補償數據。

在傳輸事務 eefac7d8-2370-11e9-bfeb-000c29d74445:3 的時候,從實例的 io_thread 發現這個事務記錄的 server_id 為 1,與自己的 server_id 一致,會認為這個是自己執行過的事務,就會把這段信息給過濾掉,最終這個從實例的數據就會缺少一部分。


總結

由此可見,在這種情況下,更換 server_id 是最簡單的解決辦法,所以我們在恢復實例的時候,儘可能的去修改 server_id,保證 server_id 不與之前的重複且與集群中的其他實例不相同。

有不少人並沒有碰到類似的問題,是因為他們在恢復實例的時候,通常是直接在線備份線上的實例,用最新的備份集來導入數據,在這種情況下,也可以避免以上問題的發生。

另外還有一個值得一提的參數 --replicate-same-server-id,這個參數的作用是開啟後即使 io_thread 收到與自己 server_id 相同的 binlog,也會寫入 relaylog。雖然啟用這個參數也可以避免以上問題,但在大部分情況下都不推薦開啟這個參數,默認值 OFF 是為了避免 binlog 在複製中迴環。在 5.7 中想開啟這個參數的話需要先關閉 log_slave_updates;8.0 則做了改進,當 gtid_mode=ON 的情況下就可以開啟。

參考:

https://lefred.be/content/mysql-gtid-restore-a-master-from-a-replicas-backup/ https://www.zhangshengrong.com/p/yOXD5zZM1B/ https://dev.mysql.com/doc/refman/5.7/en/replication-options-slave.html#optionmysqldreplicate-same-server-id


分享到:


相關文章: