主从复制进阶2

1 GTID复制

概念

GTID是全局事务ID(global transaction identifier),其保证为每一个提交的事务可以生成一个唯一的ID。当事务提交时,MySQL Server在写binlog的时候,会先写一个类型为GTID_Event的特殊Binlog Event,指定下一个事务的GTID,然后再写事务的Binlog。主从同步时GTID_Event和事务的Binlog 都会传递到从库,从库在执行的时候也是用同样的GTID写binlog,这样主从同步以后,就可通过GTID确定从库同步到的位置了。也就是说,无论是级联情况,还是一主多从情况,都可以通过GTID自动找同步位置,而无需像之前那样通过File_name和File_position找同步位置了。

GTID=server_uuid:transaction_id
server_id为发起事务的服务器的uuid,用来保证全局唯一;transaction_id是事务的编号,按照事务提交的顺序从1开始单调递增,这保证GTID在整个复制拓扑中是唯一的。
GTID复制拓扑中,无论事务被复制了多少次,事务的GTID都保持不变,一旦某个事务在服务器上提交后,后续所有相同GTID的事务都会被忽略,这种机制可以有效避免重复复制的现象,保持数据一致。

当master更新数据时,会在事务前产生GTID,一同记录到binlog日志中。 slave端的i/o 线程将变更的binlog写入到本地的relay log中。sql线程从relay log中获取GTID,然后对比slave端 gtid_executed集合,如果GTID存在,说明该GTID的事务已经执行,slave会忽略;如果没有记录,slave就会从relay log中执行该GTID的事务,并将GTID添加到gtid_executed集合中。

2 配置GTID

在线切换为GTID,假设主从复制正在运行,可以在线切换为GTID复制,此方法不需要重启Mysql实例,非常适合生产环境使用.

步骤1:检验GTID 模式复制的兼容性

在主master和从slave上执行

mysql> select @@enforce_gtid_consistency;
+----------------------------+
| @@enforce_gtid_consistency |
+----------------------------+
| OFF                        |
+----------------------------+
1 row in set (0.00 sec)
#确定GTID模式复制的兼容性:查看日志中是否有警告,如果有警告说明应用和GTID复制不兼容
mysql> set global enforce_gtid_consistency=warn;
Query OK, 0 rows affected (0.00 sec)
步骤2:启用GTID强一致性检查并且开启gtid_mode

防止GTID不兼容的语句导致复制失败

mysql> set global enforce_gtid_consistency=on;
Query OK, 0 rows affected (0.00 sec)
#将主和从的gtid_mode一步步从OFF <-> OFF_PERMISSIVE <-> ON_PERMISSIVE <-> ON
mysql> set global gtid_mode=OFF_PERMISSIVE;
Query OK, 0 rows affected (0.00 sec)

mysql> set global gtid_mode=ON_PERMISSIVE;
Query OK, 0 rows affected (0.00 sec)

mysql> set global gtid_mode=ON;
Query OK, 0 rows affected (0.00 sec)
#在主库和从库上等待所有匿名事务复制完成(该变量为0)
mysql> show status like 'ongoing_anonymous_transaction_count';
+-------------------------------------+-------+
| Variable_name                       | Value |
+-------------------------------------+-------+
| Ongoing_anonymous_transaction_count | 0     |
+-------------------------------------+-------+
1 row in set (0.00 sec)
步骤3:启用GTID自动定位
mysql> stop replica;
mysql> CHANGE REPLICATION SOURCE TO SOURCE_AUTO_POSITION = 1;
mysql> start replica;
mysql> show replica status \G
           Replica_IO_Running: Yes
          Replica_SQL_Running: Yes
检测

主库上看

mysql> stop replica;
mysql> CHANGE REPLICATION SOURCE TO SOURCE_AUTO_POSITION = 1;
mysql> start replica;
mysql> show replica status \G
           Replica_IO_Running: Yes
          Replica_SQL_Running: Yes
mysql> show master status\G
*************************** 1. row ***************************
             File: binlog.000005
         Position: 535
     Binlog_Do_DB:
 Binlog_Ignore_DB:
Executed_Gtid_Set: 4233a222-0a2d-4f46-a2ad-a3b36b351b07:1-2
1 row in set (0.00 sec)
mysql> show binlog events in 'binlog.000005'\G
*************************** 1. row ***************************
   Log_name: binlog.000005
        Pos: 4
 Event_type: Format_desc
  Server_id: 129
End_log_pos: 126
       Info: Server ver: 8.0.40, Binlog ver: 4
*************************** 2. row ***************************
   Log_name: binlog.000005
        Pos: 126
 Event_type: Previous_gtids
  Server_id: 129
End_log_pos: 157
       Info:
*************************** 3. row ***************************
   Log_name: binlog.000005
        Pos: 157
 Event_type: Gtid
  Server_id: 129
End_log_pos: 234
       Info: SET @@SESSION.GTID_NEXT= '4233a222-0a2d-4f46-a2ad-a3b36b351b07:1'
*************************** 4. row ***************************
   Log_name: binlog.000005
        Pos: 234
 Event_type: Query
  Server_id: 129
End_log_pos: 345
       Info: create database test2 /* xid=108 */
*************************** 5. row ***************************
   Log_name: binlog.000005
        Pos: 345
 Event_type: Gtid
  Server_id: 129
End_log_pos: 422
       Info: SET @@SESSION.GTID_NEXT= '4233a222-0a2d-4f46-a2ad-a3b36b351b07:2'
*************************** 6. row ***************************
   Log_name: binlog.000005
        Pos: 422
 Event_type: Query
  Server_id: 129
End_log_pos: 535
       Info: use `test2`; create table t1(id int) /* xid=113 */
6 rows in set (0.01 sec)
#查看已经执行过的gtid
mysql> SELECT @@GTID_EXECUTED;
+------------------------------------------+
| @@GTID_EXECUTED                          |
+------------------------------------------+
| 4233a222-0a2d-4f46-a2ad-a3b36b351b07:1-2 |
+------------------------------------------+
1 row in set (0.00 sec)

从库上看

mysql> show replica status\G
*************************** 1. row ***************************
             Replica_IO_State: Waiting for source to send event
                  Source_Host: 192.168.168.129
                  Source_User: rep
                  Source_Port: 3306
                Connect_Retry: 60
              Source_Log_File: binlog.000005
          Read_Source_Log_Pos: 535
               Relay_Log_File: master-relay-bin.000002
                Relay_Log_Pos: 745
        Relay_Source_Log_File: binlog.000005
           Replica_IO_Running: Yes
          Replica_SQL_Running: Yes
#查看relaylog日志
mysql> show relaylog events in "master-relay-bin.000002"\G
*************************** 1. row ***************************
   Log_name: master-relay-bin.000002
        Pos: 4
 Event_type: Format_desc
  Server_id: 200
End_log_pos: 126
       Info: Server ver: 8.0.40, Binlog ver: 4
*************************** 2. row ***************************
   Log_name: master-relay-bin.000002
        Pos: 126
 Event_type: Previous_gtids
  Server_id: 200
End_log_pos: 157
       Info:
*************************** 3. row ***************************
   Log_name: master-relay-bin.000002
        Pos: 157
 Event_type: Rotate
  Server_id: 129
End_log_pos: 0
       Info: binlog.000005;pos=4
*************************** 4. row ***************************
   Log_name: master-relay-bin.000002
        Pos: 201
 Event_type: Format_desc
  Server_id: 129
End_log_pos: 126
       Info: Server ver: 8.0.40, Binlog ver: 4
*************************** 5. row ***************************
   Log_name: master-relay-bin.000002
        Pos: 323
 Event_type: Rotate
  Server_id: 0
End_log_pos: 0
       Info: binlog.000005;pos=157
*************************** 6. row ***************************
   Log_name: master-relay-bin.000002
        Pos: 367
 Event_type: Gtid
  Server_id: 129
End_log_pos: 234
       Info: SET @@SESSION.GTID_NEXT= '4233a222-0a2d-4f46-a2ad-a3b36b351b07:1'
*************************** 7. row ***************************
   Log_name: master-relay-bin.000002
        Pos: 444
 Event_type: Query
  Server_id: 129
End_log_pos: 345
       Info: create database test2 /* xid=108 */
*************************** 8. row ***************************
   Log_name: master-relay-bin.000002
        Pos: 555
 Event_type: Gtid
  Server_id: 129
End_log_pos: 422
       Info: SET @@SESSION.GTID_NEXT= '4233a222-0a2d-4f46-a2ad-a3b36b351b07:2'
*************************** 9. row ***************************
   Log_name: master-relay-bin.000002
        Pos: 632
 Event_type: Query
  Server_id: 129
End_log_pos: 535
       Info: use `test2`; create table t1(id int) /* xid=113 */
9 rows in set (0.00 sec)

半同步复制

概念

说到半同步复制就不得不提到异步复制、半同步复制、全同步复制。

在早期的Mysql5.5版本之前一直采用的是异步复制方式,异步复制就是主把事务写到binlog日志后并不管从是否接收或者什么时候接收,即主库的事务执行不会管备库的同步进度,如果备库落后而此时主库不幸crash,那么就会导致数据丢失。

在Mysql5.5引入了半同步复制,主库在应答客户端提交的事务前需要保证至少一个从库接收并将事务写到relay log中,即master将写入binlog的事务传递到slave,主库提交事务,master等待slave回复ACK确认已将数据写入relay log,此时master才会将commit OK结果反馈给客户端,如果出现异常没有收到ACK,那么将自动降级为普通的复制,直到异常修复后又会变为半同步复制。

半同步复制的特性:

半同步复制必须是在主库和从库两端都开启时才行,从库会在连接到主库时告诉主库它是不是配置了半同步。

如果半同步复制在主库端是开启了的,并且至少有一个半同步复制的从库节点,那么此时主库的事务线程在提交时会被阻塞并等待,等待的结果有两种,第一种是至少一个从库节点通知主库自己已经收到了所有这个事务的binlog事件;第二种就是主库一直等待直到超过配置的某一个时间点为止,此种情况下半同步复制将自动关闭,转换为异步复制。

从库节点只有在接收到某一个事务的所有binlog并将其写入relay log文件之后才会通知主库。

在mysql5.5-5.6使用after_commit的半同步模式下,客户端事务在存储引擎层提交后,在得到从库确认的过程中,主库宕机了,此时主库在等待slave ACK的时候,虽然没有返回到当前客户端但是事务已经提交,其他客户端会读取到已经提交的事务,如果slave端还没有读到该事务的events,同时主库发生了crash,然后切换到备库,那么之前读到的事务就不见了。如果主库永远启动不了,那么实际上在主库已经成功提交的事务在从库上是找不到的,也就是数据丢失了。

为了解决上述问题,在mysql5.7版本增加了after_sync(无损复制)参数,并将其设置为默认的半同步方式。在mysql5.7.2引入的Loss-less Semi-Synchronous(无损复制)通过添加参数after_sync,使得主库在commit事务之前等待slave的ACK,只有slave回复了ACK,主库才会提交事务,这样就保证了master和slave数据的一致性。

在mysql5.7.17又引入了全同步技术,全同步复制就是当主库提交事务之后,所有的从库节点必须收到并且apply这些事务,然后主库线程才能继续后续操作。

1 主的配置

#加载插件
mysql> install plugin rpl_semi_sync_source soname 'semisync_source.so';
Query OK, 0 rows affected (0.03 sec)
#查看插件是否加载成功,出现下面这一行表示加载成功
mysql> show plugins;

| rpl_semi_sync_source            | ACTIVE   | REPLICATION        | semisync_sou                                                                                   rce.so | GPL     |
+---------------------------------+----------+--------------------+-------------                                                                                   -------+---------+
#开启此开关,可以写进配置文件
mysql> set global rpl_semi_sync_source_enabled=1;
Query OK, 0 rows affected (0.00 sec)
#设置主等待从回复ACK的超时时间为3秒,默认是10秒,可写入配置文件
mysql> set global rpl_semi_sync_source_timeout=3000;
Query OK, 0 rows affected (0.00 sec)

2 从的配置

mysql> install plugin rpl_semi_sync_replica soname 'semisync_replica.so';
Query OK, 0 rows affected (0.01 sec)

mysql> set global rpl_semi_sync_replica_enabled=1;
Query OK, 0 rows affected (0.00 sec)
#查看插件是否加载成功
mysql> show plugins;
#重启从的IO线程
mysql> STOP REPLICA IO_THREAD;
mysql> START REPLICA IO_THREAD;
mysql> show status like 'rpl_semi%';
+------------------------------+-------+
| Variable_name                | Value |
+------------------------------+-------+
| Rpl_semi_sync_replica_status | ON    |
+------------------------------+-------+

3 测试

主操作

mysql> use test;
Database changed
mysql> show tables;
Empty set (0.00 sec)

mysql> create table t1(id int);
Query OK, 0 rows affected (0.01 sec)

mysql> show status like 'rpl_semi_sync_source_yes_tx';
+-----------------------------+-------+
| Variable_name               | Value |
+-----------------------------+-------+
| Rpl_semi_sync_source_yes_tx | 1     |  #master成功接收到slave的回复的次数
+-----------------------------+-------+
1 row in set (0.01 sec)

模拟从库故障

mysql> stop REPLICA io_thread;
mysql> set global rpl_semi_sync_replica_enabled=0;
mysql>  show status like 'rpl_semi_sync_replica_status';
+------------------------------+-------+
| Variable_name                | Value |
+------------------------------+-------+
| Rpl_semi_sync_replica_status | OFF   |
+------------------------------+-------+

然后主库上插入数据,可以看到会等待一个超时时间

#超过三秒自动切换为异步复制
mysql> insert t1 values(2);
Query OK, 1 row affected (3.00 sec)
#发现主关闭了半同步复制
mysql> show status like 'rpl_semi_sync_source_status';
+-----------------------------+-------+
| Variable_name               | Value |
+-----------------------------+-------+
| Rpl_semi_sync_source_status | OFF   |
+-----------------------------+-------+
1 row in set (0.00 sec)

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐