【MySQL管理】:Replication主从复制(二):半同步复制

本文深入探讨MySQL 8.0的半同步复制技术,包括原理、特性、环境搭建、故障模拟及恢复,确保数据完整性并防止事务丢失。详细介绍了如何配置和监控半同步复制状态,以及在异常情况下如何自动切换到异步复制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

     本文介绍了MySQL半同步复制的历史、原理(包括5.6的半同步和5.7增强后的半同步),相关参数选项,实际环境搭建,半同步复制搭建步骤,模拟故障和恢复等。(以MySQL 8.0版本为例)

目录

一、半同步复制技术

二、半同步复制搭建

     1、环境介绍

    2、插件准备

    3、半同步复制

    3、半同步故障模拟

三、总结


一、半同步复制技术

      我们知道,普通的replication,即MySQL的异步复制,依靠MySQL二进制日志也即binary log进行数据复制。比如两台机器,一台主机(master),另外一台是从机(slave)。

     1)正常的复制为:事务一(t1)写入binlog buffer;dumper线程通知slave有新的事务t1;binlog buffer进行checkpoint;slave的io线程接收到t1并写入到自己的的relay log;slave的sql线程写入到本地数据库。 这时,master和slave都能看到这条新的事务,即使master挂了,slave可以提升为新的master。

     2)异常的复制为:事务一(t1)写入binlog buffer;dumper线程通知slave有新的事务t1;binlog buffer进行checkpoint;slave因为网络不稳定,一直没有收到t1;master挂掉,slave提升为新的master,t1丢失。

     3)很大的问题是:主机和从机事务更新的不同步,就算是没有网络或者其他系统的异常,当业务并发上来时,slave因为要顺序执行master批量事务,导致很大的延迟。这时slave接替Master,就会缺少数据。

       为了弥补(应该是部分弥补)以上几种场景的不足,MySQL从5.5开始推出了半同步复制。相比异步复制,半同步复制提高了数据完整性,因为很明确知道,在一个事务提交成功之后,这个事务就至少会存在于两个地方。即在master的dumper线程通知slave后,增加了一个ack(消息确认),即是否成功收到t1的标志码,也就是dumper线程除了发送t1到slave,还承担了接收slave的ack工作。如果出现异常,没有收到ack,那么将自动降级为普通的复制,直到异常修复后又会自动变为半同步复制。

半同步复制具体特性:

  • 从库会在连接到主库时告诉主库,它是不是配置了半同步。
  • 如果半同步复制在主库端是开启了的,并且至少有一个半同步复制的从库节点,那么此时主库的事务线程在提交时会被阻塞并等待,结果有两种可能,要么至少一个从库节点通知它已经收到了所有这个事务的Binlog事件,要么一直等待直到超过配置的某一个时间点为止,而此时,半同步复制将自动关闭,转换为异步复制。(异步复制兜底)
  • 从库节点只有在接收到某一个事务的所有Binlog,将其写入并Flush到Relay Log文件之后(写到Relay log文件既可,不等待Slave加载应用到数据库中),然后通知对应主库上面的等待线程。
  • 如果在等待过程中,等待时间已经超过了配置的超时时间,没有任何一个从节点通知当前事务,那么此时主库会自动转换为异步复制,当至少一个半同步从节点赶上来时,主库便会自动转换为半同步方式的复制。
  • 半同步复制必须是在主库和从库两端都开启时才行,如果在主库上没打开,或者在主库上开启了而在从库上没有开启,主库都会使用异步方式复制。

半同步复制仍前置问题:

   

MySQL 5.7半同步复制技术

    如上图,半同步复制原理图(MySQL 5.7之前的),Master将每个事务写入binlog(sync_binlog=1),传递到slave刷新到磁盘(sync_relay=1),同时主库提交事务(commit)。master等待slave反馈收到relay log,只有收到ACK后master才将commit OK结果反馈给客户端。

    在MySQL 5.5~5.6使用AFTER_COMMIT的选项下,既Master先commit再等待Slave ACK:客户端事务在存储引擎层COMMIT后,在等待从库确认的过程中,这时主库宕机了。此时,即主库在等待Slave ACK的时候,虽然Master没有返回当前客户端已提交完毕,但实际上Master库的事务已经提交并写入磁盘,其他客户端若读取则会读取到已提交事务。

    如果Slave端还没有读到该事务的events(既Slave还没有跟上主库),就在这时,主库发生了crash,然后切换到Slave(Slave升级为主库)。那么之前其他客户端能读到的事务就不见了,出现了幻读。如下图所示,图片引自Loss-less Semi-Synchronous Replication on MySQL 5.7.2

MySQL 5.7半同步复制技术

       如果主库永远启动不了,那么实际上在主库已经成功提交的事务,在从库上是永远找不到的,也就是数据丢失了,这是MySQL不愿意看到的。所以在MySQL 5.7版本中增加了AFTER_SYNC选项,并将其设置为默认半同步方式,一定程度上避免了此问题。 

    因此,建议半同步一律设置为AFTER_SYNC,不要再设置为AFTER_COMMIT了。

二、半同步复制搭建

     1、环境介绍

IP操作系统数据库版本用途
192.168.43.201CentOS 7.7 x64bitMySQL 8.0.18,已有数据作为Master主节点
192.168.43.203CentOS 7.7 x64bitMySQL 8.0.18,全新库作为201的Slave从节点

主从复制环境,应保证操作系统和数据库版本一致,减少出问题的概率。

    2、插件准备

      由于半同步是以插件形式的实现的,所有需要安装插件。

     1、判断MySQL服务器是否支持动态增加插件。分别在Master节点和Slave查询全局变量:

mysql> show global variables like 'have_dynamic_loading';
+----------------------+-------+
| Variable_name        | Value |
+----------------------+-------+
| have_dynamic_loading | YES   |
+----------------------+-------+
1 row in set (0.01 sec)

     2、检查MySQL安装目录下是否已经存在插件。插件一般默认在$MYSQL_HOME/lib/plugin目录下,插件的文件名为*.so文件。 半同步主库的插件为semisync_master.so,半同步从库的插件为semisync_slave.so。  建议无论主库从库,两个插件都安装。这样发生主从切换后,半同步仍可以使用。   分别在Master节点和Slave上安装:

mysql> install plugin rpl_semi_sync_master soname 'semisync_master.so';
Query OK, 0 rows affected (0.00 sec)

mysql> install plugin rpl_semi_sync_slave soname 'semisync_slave.so';
Query OK, 0 rows affected (0.00 sec)

mysql> select * from mysql.plugin;
+----------------------+--------------------+
| name                 | dl                 |
+----------------------+--------------------+
| rpl_semi_sync_master | semisync_master.so |
| rpl_semi_sync_slave  | semisync_slave.so  |
+----------------------+--------------------+
2 rows in set (0.00 sec)

    3、查看当前的半同步的参数值:

mysql> show global variables like '%semi_sync%';
+-------------------------------------------+------------+
| Variable_name                             | Value      |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled              | OFF        |
| 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 |
| rpl_semi_sync_slave_enabled               | OFF        |
| rpl_semi_sync_slave_trace_level           | 32         |
+-------------------------------------------+------------+
8 rows in set (0.00 sec)

   可以看到:

  • rpl_semi_sync_master_enabled=OFF   主库上,半同步当前未打开,未启用。
  • rpl_semi_sync_master_timeout=10000   主库等待从库ACK的超时时间,单位毫秒(默认值为10秒)。超过此时间将自动降为异步复制。
  • rpl_semi_sync_master_trace_level=32  主库上半同步的trace debug级别。取值具体见官方文档。
  • rpl_semi_sync_master_wait_for_slave_count=1 主库需要等待的ACK应答的Slave库个数,默认为1。
  • rpl_semi_sync_master_wait_no_slave=ON slave少了是,master是否继续等(不转为异步)。为OFF时,只要master发现【Rpl_semi_sync_master_clients】(见后文)小于【rpl_semi_sync_master_wait_for_slave_count】,则master立即转为异步模式。为ON时,空闲时间(无事务提交)里,即使master发现【Rpl_semi_sync_master_clients】小于rpl_semi_sync_master_wait_for_slave_count】,也不会做任何调整。只要保证在事务超时之前,master收到大于等于【rpl_semi_sync_master_wait_for_slave_count】值的ACK应答数量,master就一直保持在半同步模式;如果在事务提交阶段(master等待ACK)超时,master才会转为异步模式。
  • rpl_semi_sync_master_wait_point=AFTER_SYNC。 既上文提到的5.7后半同步的改进。 5.6之前为AFTER_COMMIT。建议保持AFTER_SYNC。
  • rpl_semi_sync_slave_enabled=OFF 从库上,半同步当前未打开,未启用。
  • rpl_semi_sync_slave_trace_level=32  从库上,半同步的trace debug级别。取值具体见官方文档

  可以发现规律以【rpl_semi_sync_master】开头为主库上的参数,以【rpl_semi_sync_slave】为从库上的参数。

    3、半同步复制

     1、主库开启同步复制:

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

     2、从库开启同步复制:

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

     3、参考异步复制在Master主库上建立用户,grant授权,在从库上执行 change master to 等操作:

步骤略,参见《异步复制》文章

     4、在从库上start slave启动复制后。在主库、从库分别查看stauts状态:

(以下为主库的status,实际上最后一个salve_status不是主库的状态,只因为在主库上也安装了半同步slave插件,所以才有了最后一行)
mysql> show status like '%semi_sync%';
+--------------------------------------------+-------+
| Variable_name                              | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients               | 1     |
| Rpl_semi_sync_master_net_avg_wait_time     | 0     |
| Rpl_semi_sync_master_net_wait_time         | 0     |
| Rpl_semi_sync_master_net_waits             | 1     |
| Rpl_semi_sync_master_no_times              | 1     |
| Rpl_semi_sync_master_no_tx                 | 4     |
| 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     |
| Rpl_semi_sync_slave_status                 | OFF   |
+--------------------------------------------+-------+
15 rows in set (0.00 sec)


(以下为主库的status,实际上从库上只有一个status状态。其他为主库才有的。只因为在从库上也安装了半同步master插件,所以才显示这些带master,但值不会变化。)
mysql>  show status like '%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                | OFF   |
| 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     |
| Rpl_semi_sync_slave_status                 | ON    |
+--------------------------------------------+-------+
15 rows in set (0.00 sec)

以上名字中带master是主库才有的信息,带slave是从库才有的。(因为插件我们master和slave都安装了,所以都显示出来了,但不是自己的内容,其值永远不变)。

需要重点关注:

  • Rpl_semi_sync_master_client  显示为当前连接到master进行半同步的slave数量。
  • 而且Rpl_semi_sync_master_status=ON,Rpl_semi_sync_slave_status=ON表示主库、从库都处于半同步状态。
  • Rpl_semi_sync_master_yes_tx=0  表示通过半同步,复制至主库的事务数为0
  • Rpl_semi_sync_master_no_tx=4  表示不是通过半同步,复制至主库的事务数为4

     5、在主库上进行以下DML操作,修改一些数据。然后在主库上查看stauts状态:

mysql> show status like '%semi_sync%';
+--------------------------------------------+-------+
| Variable_name                              | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients               | 1     |
| Rpl_semi_sync_master_net_avg_wait_time     | 0     |
| Rpl_semi_sync_master_net_wait_time         | 0     |
| Rpl_semi_sync_master_net_waits             | 8     |
| Rpl_semi_sync_master_no_times              | 1     |
| Rpl_semi_sync_master_no_tx                 | 4     |
| Rpl_semi_sync_master_status                | ON    |
| Rpl_semi_sync_master_timefunc_failures     | 0     |
| Rpl_semi_sync_master_tx_avg_wait_time      | 656   |
| Rpl_semi_sync_master_tx_wait_time          | 4594  |
| Rpl_semi_sync_master_tx_waits              | 7     |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0     |
| Rpl_semi_sync_master_wait_sessions         | 0     |
| Rpl_semi_sync_master_yes_tx                | 7     |
| Rpl_semi_sync_slave_status                 | OFF   |
+--------------------------------------------+-------+
15 rows in set (0.00 sec)

  可以看到。Rpl_semi_sync_master_yes_tx从0变成了7。 说明刚才修改数据,是通过半同步方式复制到了从库。


    3、半同步故障模拟

   在以上配置的基础上。rpl_semi_sync_master_timeout=10000毫秒=10秒。 我们通过在从库上停止io_thread来模拟网络故障,使得从库返回给主库的ACK超时(超过10秒),步骤如下。

     1、在从库上停止io_thread来模拟网络故障:

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

    2、在主库上执行一个事务(修改数据)并提交(默认为自动提交):

mysql> insert into tb values ('88');
Query OK, 1 row affected (10.00 sec)

     可以看到:commit提交时,等待了10秒(既为:rpl_semi_sync_master_timeout时间)

    3、再次在主库上查看stauts当前状态:

mysql> show status like '%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             | 8     |
| Rpl_semi_sync_master_no_times              | 2     |
| Rpl_semi_sync_master_no_tx                 | 5     |
| Rpl_semi_sync_master_status                | OFF   |
| Rpl_semi_sync_master_timefunc_failures     | 0     |
| Rpl_semi_sync_master_tx_avg_wait_time      | 656   |
| Rpl_semi_sync_master_tx_wait_time          | 4594  |
| Rpl_semi_sync_master_tx_waits              | 7     |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0     |
| Rpl_semi_sync_master_wait_sessions         | 0     |
| Rpl_semi_sync_master_yes_tx                | 7     |
| Rpl_semi_sync_slave_status                 | OFF   |
+--------------------------------------------+-------+
15 rows in set (0.00 sec)

    可以看到:

  • Rpl_semi_sync_master_yes_tx没有增加,Rpl_semi_sync_master_no_tx增加了,增加了一个事务。
  • Rpl_semi_sync_master_status=OFF,半同步关闭了,切换到了异步复制状态。

    4、然后在从库中start slave; 开启从库的复制。 模拟网络恢复。然后在主库上查看stauts状态:

mysql> show status like '%semi_sync%';
+--------------------------------------------+-------+
| Variable_name                              | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients               | 1     |
| Rpl_semi_sync_master_net_avg_wait_time     | 0     |
| Rpl_semi_sync_master_net_wait_time         | 0     |
| Rpl_semi_sync_master_net_waits             | 9     |
| Rpl_semi_sync_master_no_times              | 2     |
| Rpl_semi_sync_master_no_tx                 | 5     |
| Rpl_semi_sync_master_status                | ON    |
| Rpl_semi_sync_master_timefunc_failures     | 0     |
| Rpl_semi_sync_master_tx_avg_wait_time      | 656   |
| Rpl_semi_sync_master_tx_wait_time          | 4594  |
| Rpl_semi_sync_master_tx_waits              | 7     |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0     |
| Rpl_semi_sync_master_wait_sessions         | 0     |
| Rpl_semi_sync_master_yes_tx                | 7     |
| Rpl_semi_sync_slave_status                 | OFF   |
+--------------------------------------------+-------+
15 rows in set (0.00 sec)

    可以看到:Rpl_semi_sync_master_status=ON,自动恢复为半同步。

    5、在主库上,再次执行一个事务(修改数据)并提交。然后在主库上查看stauts状态:

mysql> insert into tb values ('99');
Query OK, 1 row affected (0.00 sec)

mysql> show status like '%semi_sync%';
+--------------------------------------------+-------+
| Variable_name                              | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients               | 1     |
| Rpl_semi_sync_master_net_avg_wait_time     | 0     |
| Rpl_semi_sync_master_net_wait_time         | 0     |
| Rpl_semi_sync_master_net_waits             | 10    |
| Rpl_semi_sync_master_no_times              | 2     |
| Rpl_semi_sync_master_no_tx                 | 5     |
| Rpl_semi_sync_master_status                | ON    |
| Rpl_semi_sync_master_timefunc_failures     | 0     |
| Rpl_semi_sync_master_tx_avg_wait_time      | 643   |
| Rpl_semi_sync_master_tx_wait_time          | 5147  |
| Rpl_semi_sync_master_tx_waits              | 8     |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0     |
| Rpl_semi_sync_master_wait_sessions         | 0     |
| Rpl_semi_sync_master_yes_tx                | 8     |
| Rpl_semi_sync_slave_status                 | OFF   |
+--------------------------------------------+-------+
15 rows in set (0.00 sec)

   可以看到:自动恢复为半同步后,后续有继续采用半同步复制。

三、总结

     半同步(semi_sync)是MySQL基于插件实现,主库和从库需要先安装插件,并开启。  开启后MySQL会自动优先选择通过半同步进行主从复制。当遇到网络延时等原因超过半同步的参数时,会自动切换为异步复制。

     一旦网络延时恢复,MySQL会自动恢复为半同步模式。
   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值