暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

错误使用 Change master 可能导致数据不一致

00后DBA实录 2025-01-22
20

前言

上次讲了主从原理:Keyring 搭建主从

这次讲一个容易被忽略的 Change master
 细节吧。

先讲现象:如果主从存在延迟,这时执行 stop slave
 + change master
 将清空 relay log 。存在数据不一致的风险。

开始测试,后面教怎么避免。

TEST

stop slave

有一主两从 3 台主机 30 是主

192.168.58.30(192.168.58.30:3306) (current master)
 +--192.168.58.40(192.168.58.40:3306)
 +--192.168.58.50(192.168.58.50:3306)

先测试 slave 1

root@localhost [(none)] 00:22:55 > show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for source to send event
                  Master_Host: 192.168.58.30
                  Master_User: replica
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000002
          Read_Master_Log_Pos: 9288
               Relay_Log_File: mysql-relay.000005
                Relay_Log_Pos: 9504
        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: 9288
              Relay_Log_Space: 9966
              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: 30
                  Master_UUID: 541e12fa-d5f5-11ed-8994-000c29c34822
             Master_Info_File: mysql.slave_master_info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Replica 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: 541e12fa-d5f5-11ed-8994-000c29c34822:1-79
            Executed_Gtid_Set: 541e12fa-d5f5-11ed-8994-000c29c34822:1-79
                Auto_Position: 1
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version: 
       Master_public_key_path: 
        Get_master_public_key: 1
            Network_Namespace: 
1 row in set1 warning (0.00 sec)


看几个关键的指标就可以了:

  • Master_Host: 192.168.58.30
  • Master_Log_File: mysql-bin.000002
  • Read_Master_Log_Pos: 9288
  • Relay_Log_File: mysql-relay.000005
  • Relay_Log_Pos: 9504

先执行一下常规 stop slave
 和 change master

stop slave;
change master to Master_Host='192.168.58.30';
start slave;

再看一下关键指标

root@localhost [(none)] 00:35:00 > show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for source to send event
                  Master_Host: 192.168.58.30
                  ······
              Master_Log_File: mysql-bin.000002
          Read_Master_Log_Pos: 9288
               Relay_Log_File: mysql-relay.000002
                Relay_Log_Pos: 420
        Relay_Master_Log_File: mysql-bin.000002
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
                  ······
          Exec_Master_Log_Pos: 9288
                  ······
1 row in set1 warning (0.01 sec)

Master_Log_File: mysql-bin.000002
  和 Read_Master_Log_Pos: 9288
 GTID 自动定位自动获取了源的  binlog

Relay_Log_File: mysql-relay.000002
 和 Relay_Log_Pos: 420
 relay log 与刚才不同,为什么?因为这已经是被清除过一次之后的重置后的 relay log,与动漫里面经常有的占领躯壳的灵魂一个效果,已经不是刚才的 relay log了,相当于这些数据都丢了。

stop slave io_thread

先看 relay log 文件有几个

[root@slave2 mysql_8_3306]# ll *relay*
-rw-r-----. 1 mysql mysql 16209 1月  22 00:47 mysql-relay.000003
-rw-r-----. 1 mysql mysql  9504 1月  22 00:47 mysql-relay.000004
-rw-r-----. 1 mysql mysql    76 1月  22 00:47 mysql-relay.index

再用另一台 slave 测试 stop slave io_thread;
 和 stop slve sql_thread;

root@localhost [(none)] 00:42:07 > show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for source to send event
                  Master_Host: 192.168.58.30
                  ······
              Master_Log_File: mysql-bin.000002
          Read_Master_Log_Pos: 9288
               Relay_Log_File: mysql-relay.000004
                Relay_Log_Pos: 9504
        Relay_Master_Log_File: mysql-bin.000002
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
                  ······
          Exec_Master_Log_Pos: 9288
              Relay_Log_Space: 25713
                  ······
1 row in set1 warning (0.00 sec)

这次只 stop 掉 iothread

stop slave io_thread;
change master to Master_Host='192.168.58.30';
start slave io_thread;

查看:

root@localhost [(none)] 00:43:46 > show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for source to send event
                  Master_Host: 192.168.58.30
                  ······
              Master_Log_File: mysql-bin.000002
          Read_Master_Log_Pos: 9288
               Relay_Log_File: mysql-relay.000005
                Relay_Log_Pos: 460
        Relay_Master_Log_File: mysql-bin.000002
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
                  ······
          Exec_Master_Log_Pos: 9288
              Relay_Log_Space: 10013
                  ······
1 row in set1 warning (0.00 sec)

可以看到 Relay_Log_File: mysql-relay.000005
 和  Relay_Log_Pos: 460
 都变了,切了一个 log ,log 数字加了 1。

[root@slave2 mysql_8_3306]# ll *relay*
-rw-r-----. 1 mysql mysql 9553 1月  22 00:49 mysql-relay.000004
-rw-r-----. 1 mysql mysql  460 1月  22 00:49 mysql-relay.000005
-rw-r-----. 1 mysql mysql   76 1月  22 00:49 mysql-relay.index

查看文件数发现,已经被读取的 mysql-relay.000003
 被清除了。

因为 mysql-relay.000003
 是已经被重放过的,当 relay_log_purge
 为 ON 时,重新 change master 时发现 mysql-relay.000003
 是已经读取过的不再需要时就自动清除。

root@localhost [(none)] 00:52:25 > show variables like 'relay_log_purge';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| relay_log_purge | ON    |
+-----------------+-------+
1 row in set (0.01 sec)

我们先关闭它测试一下

set global relay_log_purge=OFF;
stop slave io_thread;
change master to Master_Host='192.168.58.30';
start slave io_thread;

查看

root@localhost [(none)] 00:52:49 > show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for source to send event
                  Master_Host: 192.168.58.30
                  ······
              Master_Log_File: mysql-bin.000002
          Read_Master_Log_Pos: 9288
               Relay_Log_File: mysql-relay.000005
                Relay_Log_Pos: 460
        Relay_Master_Log_File: mysql-bin.000002
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
                  ······
          Exec_Master_Log_Pos: 9288
              Relay_Log_Space: 26222
                  ······
1 row in set1 warning (0.01 sec)

这些指标都没变化,但是 mysql-relay.000003
 不再被清除:

[root@slave2 mysql_8_3306]# ll *relay*
-rw-r-----. 1 mysql mysql 16209 1月  22 00:51 mysql-relay.000003
-rw-r-----. 1 mysql mysql  9553 1月  22 00:52 mysql-relay.000004
-rw-r-----. 1 mysql mysql   460 1月  22 00:52 mysql-relay.000005
-rw-r-----. 1 mysql mysql   114 1月  22 00:52 mysql-relay.index

这里主要告诉你们清除了已经执行的是正常的。

stop sql_thread

只关 sql 线程可以操作的比较有限我这里就测一个:

stop slave sql_thread;
change master to SOURCE_DELAY=660;
start slave sql_thread;

同样没变:

              Master_Log_File: mysql-bin.000002
          Read_Master_Log_Pos: 9288
               Relay_Log_File: mysql-relay.000005
                Relay_Log_Pos: 460
        Relay_Master_Log_File: mysql-bin.000002

但是执行 stop slave
 就没了

stop slave;
change master to SOURCE_DELAY=660;
start slave;

              Master_Log_File: mysql-bin.000002
          Read_Master_Log_Pos: 9288
               Relay_Log_File: mysql-relay.000002
                Relay_Log_Pos: 420
        Relay_Master_Log_File: mysql-bin.000002

且不受 relay_log_purge
 控制,机制是这样的。

root@localhost [(none)] 01:07:06 > show variables like 'relay_log_purge';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| relay_log_purge | OFF   |
+-----------------+-------+
1 row in set (0.01 sec)

[root@slave2 mysql_8_3306]# ll *relay*
-rw-r-----. 1 mysql mysql 206 1月  22 01:04 mysql-relay.000001
-rw-r-----. 1 mysql mysql 420 1月  22 01:04 mysql-relay.000002
-rw-r-----. 1 mysql mysql  76 1月  22 01:04 mysql-relay.index

原理

如果指定 stop slave
 + relay
 的文件位置或 pos 也不会清空,测试不再赘述。

官网内容:

当复制 SQL 线程停止时,可以在正在运行的副本上执行使用 RELAY_LOG_FILE
RELAY_LOG_POS
 或两者的 CHANGE MASTER TO 语句。如果复制 SQL(应用程序)线程和复制 I/O(接收器)线程中至少有一个正在运行,则会保留中继日志。如果两个线程都停止,则除非指定了 RELAY_LOG_FILE
 或 RELAY_LOG_POS
 中的至少一个,否则所有中继日志文件都将被删除。


https://dev.mysql.com/doc/refman/8.0/en/change-master-to.html

Change master 时会清空历史  relay log 未重放日志的条件:

  1. 执行 stop slave
  2. change master 时未指定 RELAY_LOG_FILE
     或  RELAY_LOG_POS

清理已重放日志受 relay_log_purge
 控制。

结论

这个时候就有一个疑问了:所以,生产我们该怎么操作?

  1. 执行 stop slave io_thread;
  2. 确认 Exec_Master_Log_Pos
    和 Read_Master_Log_Pos
     一致
  3. stop slave sql_thread;
    ,或者有些只需要停 IO 即可就不需要这一步了。
  4. 自行判断是否关闭 relay_log_purge
  5. chanage master

往期回顾


感谢阅读!关注我获取更多精彩内容。


文章转载自00后DBA实录,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论