三、半同步复制与 GTID 实践笔记

3.1 半同步复制原理
- Master 执行事务后,暂不提交,等待至少一个 Slave 返回 ACK。
- Slave 的 IO 线程接收 Binlog 并写入 Relay Log 后,向 Master 返回 ACK。
- Master 收到 ACK 后提交事务,并向客户端返回成功。
- 若在超时时间内未收到 ACK(默认 10 秒),自动降级为异步复制。
- MySQL 5.6 使用
after_commit模式(先提交再等 ACK),存在数据风险。MySQL 5.7+ 默认使用更安全的AFTER_SYNC模式。
3.2 gtid 模式
当为启用 gtid 时我们要考虑的问题
在 master 端的写入时多用户读写,在 slave 端的复制时单线程日志回放,所以 slave 端一定会延迟与 master 端
这种延迟在 slave 端的延迟可能会不一致,当 master 挂掉后 slave 接管,一般会挑选一个和 master 延迟日志最接近的充当新的 master
那么为接管 master 的主机继续充当 slave 角色并会指向到新的 master 上,作为其 slave
这时候按照之前的配置我们需要知道新的 master 上的 pos 的 id,但是我们无法确定新的 master 和 slave 之间差多
当激活 GITD 之后
当 master 出现问题后,slave2 和 master 的数据最接近,会被作为新的 master
slave1 指向新的 master,但是他不会去检测新的 master 的 pos id,只需要继续读取自己 gtid_next 即可
GTID 的简单工作流程
- 主库执行一个事务,提交后自动生成一个唯一的 GTID,记录到 binlog 里;
- 从库读取主库的 binlog,先记录这个 GTID(标记为 "已收到");
- 从库执行这个事务,执行完后把 GTID 标记为 "已执行";
- 主从同步时,从库只会向主库请求自己 "未执行" 的 GTID 对应的事务。
设置 gtid
#在master端和slave端开启gtid模式
[root@mysql-node1 ~]# vim /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
server-id=1
log-bin=mysql-bin
gtid_mode=ON
enforce-gtid-consistency=ON
symbolic-links=0
[root@mysql-node1 ~]# /etc/init.d/mysqld restart

[root@mysql-node2 ~]# vim /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
server-id=2
log-bin=mysql-bin
gtid_mode=ON
enforce-gtid-consistency=ON
symbolic-links=0
[root@mysql-node2 ~]# /etc/init.d/mysqld restart

[root@mysql-node3 ~]# vim /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
server-id=3
log-bin=mysql-bin
gtid_mode=ON
enforce-gtid-consistency=ON
symbolic-links=0
三台主机全部执行 /etc/init.d/mysqld restart

#查看gtid状态
mysql> show variables like '%gtid%';
+----------------------------------+-----------+
| Variable_name | Value |
+----------------------------------+-----------+
| binlog_gtid_simple_recovery | ON |
| enforce_gtid_consistency | ON |
| gtid_executed | |
| gtid_executed_compression_period | 0 |
| gtid_mode | ON |
| gtid_next | AUTOMATIC |
| gtid_owned | |
| gtid_purged | |
| session_track_gtids | OFF |
+----------------------------------+-----------+
9 rows in set (0.01 sec)
gtid开启前

gtid开启后

bash
#停止slave端
[root@mysql2 ~]# mysql -uroot -proot
mysql> stop slave;
Query OK, 0 rows affected (0.00 sec)
[root@mysql3 ~]# mysql -uroot -proot
mysql> stop slave;
Query OK, 0 rows affected (0.01 sec)

bash
#开启slave端的gtid
mysql> CHANGE MASTER TO MASTER_HOST='172.25.254.10', MASTER_USER='swp', MASTER_PASSWORD='swp', MASTER_AUTO_POSITIO N=1;
Query OK, 0 rows affected, 7 warnings (0.01 sec)
mysql> start slave;
Query OK, 0 rows affected, 1 warning (0.01 sec)
mysql> show slave status\G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for source to send event
Master_Host: 172.25.254.10
Master_User: swp
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000003
Read_Master_Log_Pos: 158
Relay_Log_File: mysql3-relay-bin.000002
Relay_Log_Pos: 375
Relay_Master_Log_File: mysql-bin.000003
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: 158
Relay_Log_Space: 587
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: 10
Master_UUID: 3f9a4795-332a-11f1-beae-000c29059db4
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: 10
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
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, 1 warning (0.00 sec)

3.3.启用半同步模式
在 master 端配置启用半同步模式
[root@mysql-node1 ~]# vim /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
server-id=1
log-bin=mysql-bin
gtid_mode=ON
enforce-gtid-consistency=ON
rpl_semi_sync_master_enabled=1 #开启半同步功能
symbolic-links=0

[root@mysql-node1 ~]# mysql -uroot -proot
#安装半同步插件
mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
#查看插件情况
mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS
-> FROM INFORMATION_SCHEMA.PLUGINS
-> WHERE PLUGIN_NAME LIKE '%semi%';
+----------------------+---------------+
| PLUGIN_NAME | PLUGIN_STATUS |
+----------------------+---------------+
| rpl_semi_sync_master | ACTIVE |
+----------------------+---------------+
1 row in set (0.01 sec)

#打开半同步功能
mysql> SET GLOBAL rpl_semi_sync_master_enabled = 1;
#查看半同步功能状态
mysql> SHOW VARIABLES LIKE 'rpl_semi_sync%';
+-------------------------------------------+------------+
| Variable_name | Value |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled | ON |
| rpl_semi_sync_master_timeout | 10000 |
| rpl_semi_sync_master_trace_level | 32 |
| rpl_semi_sync_master_wait_for_slave_count | 1 |
| rpl_semi_sync_master_wait_no_slave | ON |
| rpl_semi_sync_master_wait_point | AFTER_SYNC |
+-------------------------------------------+------------+
mysql> SHOW STATUS LIKE 'Rpl_semi_sync%';
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 0 |
| Rpl_semi_sync_master_net_avg_wait_time | 0 |
| Rpl_semi_sync_master_net_wait_time | 0 |
| Rpl_semi_sync_master_net_waits | 0 |
| Rpl_semi_sync_master_no_times | 0 |
| Rpl_semi_sync_master_no_tx | 0 |
| Rpl_semi_sync_master_status | ON |
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 0 |
| Rpl_semi_sync_master_tx_wait_time | 0 |
| Rpl_semi_sync_master_tx_waits | 0 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx | 0 |
+--------------------------------------------+-------+
14 rows in set (0.00 sec)
在 slave 端开启半同步功能

[root@mysql-slave和slave2都做 ~]# vim /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
server-id=1
log-bin=mysql-bin
gtid_mode=ON
enforce-gtid-consistency=ON
rpl_semi_sync_slave_enabled=1 #开启半同步功能
symbolic-links=0


[root@mysql-slave和slave2都做# mysql -uroot -proot
mysql> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
Query OK, 0 rows affected (0.01 sec)
mysql> SET GLOBAL rpl_semi_sync_slave_enabled =1;
Query OK, 0 rows affected (0.00 sec)
mysql> STOP SLAVE IO_THREAD; #重启io线程,半同步才能生效
Query OK, 0 rows affected (0.00 sec)
mysql> SHOW VARIABLES LIKE 'rpl_semi_sync%';
+---------------------------------+-------+
| Variable_name | Value |
+---------------------------------+-------+
| rpl_semi_sync_slave_enabled | ON |
| rpl_semi_sync_slave_trace_level | 32 |
+---------------------------------+-------+
2 rows in set (0.01 sec)
mysql> SHOW STATUS LIKE 'Rpl_semi_sync%';
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | ON |
+----------------------------+-------+
1 row in set (0.00 sec)

3.4.测试
在 master 端写入数据
mysql> insert into baibai.userlist values ('xixi','12340');
Query OK, 1 row affected (0.00 sec)
mysql> SHOW STATUS LIKE 'Rpl_semi_sync%';
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 2 |
| Rpl_semi_sync_master_net_avg_wait_time | 0 |
| Rpl_semi_sync_master_net_wait_time | 0 |
| Rpl_semi_sync_master_net_waits | 4 |
| Rpl_semi_sync_master_no_times | 1 |
| Rpl_semi_sync_master_no_tx #未同步数据0笔 | 0 |
| Rpl_semi_sync_master_status | ON |
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 633 |
| Rpl_semi_sync_master_tx_wait_time | 633 |
| Rpl_semi_sync_master_tx_waits | 1 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx #已同步数据1笔| 1 |
+--------------------------------------------+-------+
模拟故障:
#在slave端
[root@mysql-slave2]# mysql -uroot -proot
mysql> STOP SLAVE IO_THREAD;
Query OK, 0 rows affected (0.00 sec)
[root@mysql-slave]# mysql -uroot -proot
mysql> STOP SLAVE IO_THREAD;
Query OK, 0 rows affected (0.00 sec)

#在master端插入数据
mysql> insert into lee.userlist values ('baibai','12340');
Query OK, 1 row affected (10.00 sec) #10秒超时
mysql> SHOW STATUS LIKE 'Rpl_semi%';
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 0 |
| Rpl_semi_sync_master_net_avg_wait_time | 0 |
| Rpl_semi_sync_master_net_wait_time | 0 |
| Rpl_semi_sync_master_net_waits | 2 |
| Rpl_semi_sync_master_no_times | 1 |
| Rpl_semi_sync_master_no_tx | 1 | #一笔数据为同步
| Rpl_semi_sync_master_status | OFF | #自动转为异步模式,当slave恢复
| Rpl_semi_sync_master_timefunc_failures | 0 | #会自动恢复
| Rpl_semi_sync_master_tx_avg_wait_time | 981 |
| Rpl_semi_sync_master_tx_wait_time | 981 |
| Rpl_semi_sync_master_tx_waits | 1 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx | 1 |
+--------------------------------------------+-------+
14 rows in set (0.00 sec)

3.5 核心参数说明
| 参数 | 说明 |
|---|---|
rpl_semi_sync_master_enabled |
Master 是否启用半同步 |
rpl_semi_sync_slave_enabled |
Slave 是否启用半同步 |
rpl_semi_sync_master_timeout |
等待 ACK 超时时间(毫秒),默认 10000 |
rpl_semi_sync_master_wait_for_slave_count |
至少需要等待几个 Slave 返回 ACK |
rpl_semi_sync_master_wait_point |
AFTER_SYNC(推荐) / AFTER_COMMIT |
gtid_mode |
是否开启 GTID |
enforce_gtid_consistency |
强制 GTID 一致性,开启 GTID 时必须启用 |
3.6 最佳实践建议
- 生产环境推荐 GTID + 半同步组合使用,兼顾高可用与数据一致性。
- 设置合理的
rpl_semi_sync_master_timeout(如 5--10 秒),避免网络抖动影响写入。 - 监控
Rpl_semi_sync_master_clients和Rpl_semi_sync_master_status,及时发现半同步降级。 - 主从切换时,GTID 模式可大幅简化操作,无需手动找 position。
_COMMIT| |gtid_mode| 是否开启 GTID | |enforce_gtid_consistency` | 强制 GTID 一致性,开启 GTID 时必须启用 |
3.6 最佳实践建议
- 生产环境推荐 GTID + 半同步组合使用,兼顾高可用与数据一致性。
- 设置合理的
rpl_semi_sync_master_timeout(如 5--10 秒),避免网络抖动影响写入。 - 监控
Rpl_semi_sync_master_clients和Rpl_semi_sync_master_status,及时发现半同步降级。 - 主从切换时,GTID 模式可大幅简化操作,无需手动找 position。
- 半同步降级为异步后,待从库恢复会自动切回,无需人工干预。