mysql GTID主從複製故障後不停機恢復同步流程

Cloud發表於2020-11-30

GTID實現主從複製資料同步

GTID是一個基於原始mysql伺服器生成的一個已經被成功執行的全域性事務ID,它由伺服器ID以及事務ID組成,這個全域性事務ID不僅僅在原始伺服器上唯一,在所有主從關係的mysql伺服器上也是唯一的。正式因為這樣一個特性使得mysql主從複製變得更加簡單,以及資料庫一致性更可靠。

 

介紹

GTID的概念

  1.  全域性事務標識:global transaction identifiers
  2.  GTID是一個事務一一對應,並且全域性唯一ID
  3.  一個GTID在一個伺服器上只執行一次,避免重複執行導致資料混亂不一致
  4.  不再使用傳統的MASTER_LOG_FILE+MASTER_LOG_POS開啟複製,而是採用MASTER_AUTO_POSTION=1的方式開啟複製。
  5.  MYSQL-5.6.5及後續版本開始支援

 

GTID的組成

GTID = server_uuid:transaction_id

server_uuid:mysql伺服器的唯一標識,檢視方法mysql客戶端內:show variables like '%server_uuid%';

transaction_id:此id是當前伺服器中提交事務的一個序列號,從1開始自增長,一個數值對應一個事務

GTID號示例:c9fba9e2-db3b-11eb-81d4-000c298d8da1:1-5

 

GTID的優勢

  1.  實現主從更簡單,不用像以前一樣尋找log_file和log_pos
  2.  比傳統的主從更加安全
  3.  GTID是連續沒有空洞的,保證資料一致性,零丟失。

 

GTID工作原理

  1. master更新資料時,會在事務前產生GTID,一同記錄到binlog日誌中
  2. slave端的I/O執行緒將變更的binlog,寫入到本地的relay log中
  3. SQL執行緒從relay log中獲取GTID,然後對比slave端的binlog是否有記錄(所以MySQL5.6 slave端必須開啟binlog)
  4. 如果有記錄,說明該GTID的事務已經執行,slave會忽略
  5. 如果沒有記錄,slave就會從relay log中執行該GTID的事務,並記錄到binlog
  6. 在解析過程中會判斷是否有主鍵,如果沒有就用二級索引,如果沒有就用全部掃描

 

開始配置GTID複製

主:192.168.152.253   Centos7

從:192.168.152.252   Centos8

測試資料庫:vfan

測試表:student

 

1、修改mysql服務配置檔案,新增以下引數,隨後重啟:

server-id=100    #server id
log-bin=/var/lib/mysql/mysql-bin  #開啟binlog並指定儲存位置
expire_logs_days=10  #日誌儲存時間為10天
gtid_mode=on  #gtid模組開關
enforce_gtid_consistency=on  #啟動GTID強一致性,開啟gtid模組必須開啟此功能。
binlog_format=row  #bin_log日誌格式,共有三種STATEMENT、ROW、MIXED;預設為STATEMENT
skip_slave_start=1  #防止複製隨著mysql啟動而自動啟動

主伺服器和從伺服器的配置一致即可,server-id更改一下

 

2、在主伺服器中建立從伺服器連線的使用者

CREATE USER 'copy'@'192.168.152.252' IDENTIFIED BY 'copy';
GRANT REPLICATION SLAVE ON *.* TO 'copy'@'192.168.152.252';
flush privileges;

建立完畢記得要測試下slave機是否能登入成功

 

3、使用mysqldump使兩資料庫資料同步

主mysql執行:
mysqldump -uroot -proot1 vfan > dump2.sql
scp dump2.sql 192.168.152.252:/data/

從mysql執行:
mysql> source /data/dump2.sql

 

當前主、從伺服器資料內容一致,都是以下資料:

mysql> select * from student;
+----+------+-----+
| id | name | age |
+----+------+-----+
|  1 | Tony |  18 |
|  2 | Any  |  17 |
|  3 | Goy  |  20 |
|  4 | Baly |  18 |
|  5 | Heg  |  19 |
|  6 | hhh  | 100 |
|  7 | lll  |  99 |
+----+------+-----+
7 rows in set (0.01 sec)

 

4、開啟主從複製

mysql> CHANGE MASTER TO MASTER_HOST='192.168.152.253',MASTER_USER='copy',MASTER_PASSWORD='copy',MASTER_PORT=3306,MASTER_AUTO_POSITION=1;
Query OK, 0 rows affected, 2 warnings (0.04 sec)

mysql> start slave;
Query OK, 0 rows affected (0.01 sec)

## 檢視slave狀態
mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.152.253
                  Master_User: copy
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000014
          Read_Master_Log_Pos: 897
               Relay_Log_File: kubenode2-relay-bin.000002
                Relay_Log_Pos: 416
        Relay_Master_Log_File: mysql-bin.000014
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes

 

5、檢查是否同步

主伺服器中插入資料:
mysql> INSERT INTO student(name,age) VALUES('gogoo',50),('zhazha',25);
Query OK, 2 rows affected (0.03 sec)
Records: 2  Duplicates: 0  Warnings: 0

從伺服器中讀取:
mysql> select * from student;
+----+--------+-----+
| id | name   | age |
+----+--------+-----+
|  1 | Tony   |  18 |
|  2 | Any    |  17 |
|  3 | Goy    |  20 |
|  4 | Baly   |  18 |
|  5 | Heg    |  19 |
|  6 | hhh    | 100 |
|  7 | lll    |  99 |
|  8 | gogoo  |  50 |
|  9 | zhazha |  25 |
+----+--------+-----+
9 rows in set (0.00 sec)

資料已經同步,基礎的主從複製已經搭建完成

 

現在模擬一個主從複製架構中,從伺服器中途複製失敗,不再同步主伺服器的場景,並要求不停業務進行資料同步修復,恢復一致。

1、首先先模擬一個資料插入的場景

vim insert.sh

#!/usr/bin/env bash

values=(`find /usr/ -type d | awk -F '/' '{print $NF}' | sort -u`)

while true
do
age=$(( $RANDOM%100 ))
name=${values[$(( $RANDOM%6 ))]}

mysql -h127.1 -P3306 -uroot -proot1 -e "INSERT INTO vfan.student(name,age) VALUES('"${name}"',${age});" &> /dev/null 
sleep $(( $RANDOM%5 ))
done

執行指令碼,資料在隨機插入(插入時間間隔 < 5s)

目前主mysql資料:

mysql> select * from student;
+----+---------------------+-----+
| id | name                | age |
......
|  97 | _                   |   2 |
|  98 | 00bash              |  15 |
|  99 | 00bash              |  52 |
| 100 | 00bash              |  43 |
| 101 | _                   |  65 |
| 102 | 00                  |  67 |
+-----+---------------------+-----+
102 rows in set (0.01 sec)

 

2、資料還在陸續插入,此時模擬slave節點當機或異常(在此就直接stop slave;)

mysql> stop slave;
Query OK, 0 rows affected (0.01 sec)

 

3、此時主庫資料還在增加,而從庫已經不同步,以下是從庫資料:

mysql> select * from student;
+----+---------------------+-----+
| id | name                | age |
......
| 82 | 00bash              |  50 |
| 83 | 00systemd-bootchart |  36 |
| 84 | 00bash              |  48 |
| 85 | 00systemd-bootchart |  41 |
| 86 | 00                  |  72 |
+----+---------------------+-----+
86 rows in set (0.00 sec)

 

4、開始從庫恢復資料

思路:

 先通過mysqldump全量備份當前的資料,由於不能影響業務,所以在mysqldump資料時不能造成鎖表。要保持資料寫入

 由於mysqldump時資料還在寫入,所以有一部分資料還是會同步不全,所以匯入mysqldump的資料後,跳過dump中包含的GTID事務,再重新建立一次主從配置,開啟slave執行緒,恢復資料並同步。

 

1)mysqldump不鎖表備份資料

mysqldump -uroot -proot1 --single-transaction --master-data=2 -R vfan | gzip > dump4.sql

主要起作用引數:--single-transaction

 

2)檢視當前mysqldump匯出資料的GTID號

[root@TestCentos7 data]# grep GLOBAL.GTID_PURGED dump4.sql 
SET @@GLOBAL.GTID_PURGED=/*!80000 '+'*/ 'c9fba9e2-db3b-11eb-81d4-000c298d8da1:1-228';

以上的 c9fba9e2-db3b-11eb-81d4-000c298d8da1:1-228 表示MASTER機執行到的GTID事務號

 

3)去從資料庫匯入

scp dump4.sql 192.168.152.252:/data

mysql客戶端內:
mysql> source /data/dump4.sql

此時從庫資料:
mysql> select * from student;
| 230 | 00                  |  53 |
| 231 | 00bash              |  66 |
| 232 | _                   |  18 |
| 233 | 0.33.0              |  98 |
| 234 | 00bash              |  14 |
+-----+---------------------+-----+
234 rows in set (0.00 sec)

主庫資料:
| 454 | _                   |  46 |
| 455 | 03modsign           |  59 |
| 456 | 00systemd-bootchart |  77 |
| 457 | 03modsign           |   6 |
| 458 | 0.33.0              |  88 |
+-----+---------------------+-----+
458 rows in set (0.00 sec)

從庫資料恢復一部分到234行,主庫資料依然在增加,已經是458條

 

4)由於我們mysqldump的資料已經包含了在MASTER執行的 1-228 個事務,所以我們在SLAVE進行同步的時候,要忽略這些事務不再進行同步,不然會出現類似於這種報錯:

mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.152.253
                  Master_User: copy
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000002
          Read_Master_Log_Pos: 137827
               Relay_Log_File: kubenode2-relay-bin.000002
                Relay_Log_Pos: 417
        Relay_Master_Log_File: mysql-bin.000002
             Slave_IO_Running: Yes
            Slave_SQL_Running: No
                   Last_Errno: 1062
                   Last_Error: Could not execute Write_rows event on table vfan.student; Duplicate entry '87' for key 'student.PRIMARY', Error_code: 1062; handler error HA_ERR_FOUND_DUPP_KEY; the event's master log mysql-bin.000002, end_log_pos 10588

 

要想跳過某些GTID,SLAVE必須保證 gtid_purged 引數為空才能正確跳過,檢視當前的gtid_purged:

mysql> show global variables like '%gtid%';
+----------------------------------+-------------------------------------------------------------------------------------+
| Variable_name                    | Value                                                                               |
+----------------------------------+-------------------------------------------------------------------------------------+
| binlog_gtid_simple_recovery      | ON                                                                                  |
| enforce_gtid_consistency         | ON                                                                                  |
| gtid_executed                    | b30cb2ff-32d4-11eb-a447-000c292826bc:1-2,
c9fba9e2-db3b-11eb-81d4-000c298d8da1:1-80 |
| gtid_executed_compression_period | 1000                                                                                |
| gtid_mode                        | ON                                                                                  |
| gtid_owned                       |                                                                                     |
| gtid_purged                      | c9fba9e2-db3b-11eb-81d4-000c298d8da1:1-70                                           |
| session_track_gtids              | OFF                                                                                 |
+----------------------------------+-------------------------------------------------------------------------------------+
8 rows in set (0.02 sec)

 

當前gtid_purged不為空,所以我們要先設定它為空,執行:

mysql> reset master;
Query OK, 0 rows affected (0.05 sec)

mysql> show global variables like '%gtid%';
+----------------------------------+-------+
| Variable_name                    | Value |
+----------------------------------+-------+
| binlog_gtid_simple_recovery      | ON    |
| enforce_gtid_consistency         | ON    |
| gtid_executed                    |       |
| gtid_executed_compression_period | 1000  |
| gtid_mode                        | ON    |
| gtid_owned                       |       |
| gtid_purged                      |       |
| session_track_gtids              | OFF   |
+----------------------------------+-------+
8 rows in set (0.00 sec)

 

5)gtid_purged為空後,開始重置SLAVE

mysql> stop slave;
Query OK, 0 rows affected (0.00 sec)

mysql> reset slave all;
Query OK, 0 rows affected (0.02 sec)

 

6)重置後,設定跳過的GTID,並重新同步MASTER

mysql> SET @@GLOBAL.GTID_PURGED='c9fba9e2-db3b-11eb-81d4-000c298d8da1:1-228';
Query OK, 0 rows affected (0.01 sec)

mysql> CHANGE MASTER TO MASTER_HOST='192.168.152.253',MASTER_USER='copy',MASTER_PASSWORD='copy',MASTER_PORT=3306,MASTER_AUTO_POSITION=1;
Query OK, 0 rows affected, 2 warnings (0.04 sec)

 

7)開啟SLAVE程式,檢視同步狀態

mysql> start slave;
Query OK, 0 rows affected (0.01 sec)

mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.152.253
                  Master_User: copy
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000002
          Read_Master_Log_Pos: 137827
               Relay_Log_File: kubenode2-relay-bin.000002
                Relay_Log_Pos: 84993
        Relay_Master_Log_File: mysql-bin.000002
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 137827
              Relay_Log_Space: 85206
              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: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 100
                  Master_UUID: c9fba9e2-db3b-11eb-81d4-000c298d8da1
             Master_Info_File: mysql.slave_master_info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 
     Last_SQL_Error_Timestamp: 
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: c9fba9e2-db3b-11eb-81d4-000c298d8da1:229-519
            Executed_Gtid_Set: c9fba9e2-db3b-11eb-81d4-000c298d8da1:1-519
                Auto_Position: 1
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version: 
       Master_public_key_path: 
        Get_master_public_key: 0
            Network_Namespace: 
1 row in set (0.00 sec)

可以看到,同步正常!

 

8)最後,檢視master與slave資料是否一致

MASTER資料:SELECT * FROM student;
| 520 | 00systemd-bootchart |  18 |
| 521 | 00systemd-bootchart |  44 |
| 522 | 03modsign           |  98 |
| 523 | 00systemd-bootchart |  45 |
| 524 | 00                  |  90 |
| 525 | 03modsign           |  21 |
+-----+---------------------+-----+
525 rows in set (0.00 sec)

SLAVE資料:SELECT * FROM student;
| 519 | 0.33.0              |  99 |
| 520 | 00systemd-bootchart |  18 |
| 521 | 00systemd-bootchart |  44 |
| 522 | 03modsign           |  98 |
| 523 | 00systemd-bootchart |  45 |
| 524 | 00                  |  90 |
| 525 | 03modsign           |  21 |
+-----+---------------------+-----+
525 rows in set (0.00 sec)

在我們修過程中插入的資料也已經全部同步。資料完全一致,主從複製修復完成。

相關文章