一、半同步复制

1.原理

在半同步复制出现之前,虽然异步复制可以满足主从实例之间的数据同步,同时row 格式的binlog也能够大幅度避免主从实例的数据不一致的情况,但是如果碰到主库崩溃,写业务故障切换到从库,将从库提升为主库时,原来的主库上可能有一部分数据还没来得及被从库接收,而事实上这部分丢失的数据可能在主库上已经正常提交完成了。为解决这个问题,在MySQL 5.5版本中引入了半同步复制,半同步复制的关键改进就是当客户端在 主库上写入一个事务时,需要等待从库接收到主库的binlog,且主库接收到ACK确认之后,客户端才能收到事务成功提交的消息,如图

mysql_semi-syn_rep-1.png

这里有一个小细节需要注意:早期的半同步复制有一个缺陷,在正常的半同步复制流程中,当客户端对主库发起事务提交之后,主库发送binlog给从库,从库接收到binlog并返回ACK,然后主库返回事务提交成功的消息给发起提交的客户端。这里对于发起事务提交的客户端看起来没有任何问题,但实际上在早期的半同步复制中,主库在等待ACK 的InnoDB存储引擎内部已经提交事务,只是阻塞了返回给发起事务提交的客户端消息而已。此时如果有其他会话对该事务修改的数据进行查询,将会查询到最新数据,类似于下图

mysql_semi-syn_rep-2.png

该缺陷可能导致非发起数据提交的客户端在碰到主库故障转移时发生幻读,类似下图。User 1发起一个INSERT操作 写入一行数据,正在等待写入成功返回,此时User 2就可以在主库上查询到User 1插入的 数据,当主库发生故障,写业务切换到从库,而从库又没有收到User1写入的数据时,那 么此时对于User 1来说,会收到事务写入失败的信息,但对于User 2来说,之前在主库上能查询到的数据,切换到从库之后却发现查询不到了,就好像发生了幻读一样。

mysql_semi-syn_rep-3.png

2.开启半同步复制

主库配置:

需要提前安装好semisync_master.so

#在主库上启用半同步复制插件
rpl_semi_sync_master_enabled = 1
#等待从库发送 ACK 接收确认包的时间,如果时间超过1,则主库自动切换为异步复制。
rpl_semi_sync_master_timeout = 1000

从库配置:

需要提前安装好semisync_slave.so

#在从库上启用半同步复制插件
rpl_semi_sync_slave_enabled = 1

3.半同步复制部署

半同步复制是基于异步复制的基础上,所以在开启半同步复制前,需要先部署好异步复制。

这异步复制的搭建详见:《MySQL 异步复制》

这里直接从已部署好异步复制的服务器上做半同步复制。


主库操作:

  • 检查是否有动态支持
mysql> show global variables like 'have_dynamic_loading';
  • 安装semisync_master.so插件
mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
  • 开启半同步复制

    • 临时生效
    mysql> SET GLOBAL rpl_semi_sync_master_enabled = 1;
    mysql> SET GLOBAL rpl_semi_sync_master_timeout = 1000;
    • 永久生效(把配置卸载配置文件中,重启生效)
    [root@db01 ~]# vim /etc/my.cnf
    [mysqld]
    rpl_semi_sync_master_enabled = 1
    rpl_semi_sync_master_timeout = 1000
  • 检查半同步复制是否开启

mysql> show variables like "%semi%";

从库操作:

  • 安装semisync_slave.so插件
mysql> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
  • 开启半同步复制

    • 临时生效
    mysql> SET GLOBAL rpl_semi_sync_slave_enabled = 1;
    #重启io线程使半同步复制生效
    mysql> stop slave io_thread;
    mysql> start slave io_thread;

    注意:如果I / O线程已经在运行,并且您没有重新启动它,则从服务器将继续使用异步复制。

    • 永久生效(把配置卸载配置文件中,重启生效)
    [root@db01 ~]# vim /etc/my.cnf
    [mysqld]
    rpl_semi_sync_slave_enabled = 1
  • 检查半同步复制是否开启

mysql> show variables like "%semi%";

检查半同步状态

在主库上执行show global status like 'rpl_semi%';语句查看半同步的状态:

mysql> show global status like '%semi%';
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 1 |
| Rpl_semi_sync_master_net_avg_wait_time | 371 |
| Rpl_semi_sync_master_net_wait_time | 38649 |
| Rpl_semi_sync_master_net_waits | 104 |
| 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 | 414 |
| Rpl_semi_sync_master_tx_wait_time | 43138 |
| Rpl_semi_sync_master_tx_waits | 104 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx | 104 |
+--------------------------------------------+-------+
14 rows in set (0.00 sec)

二、增强半同步复制

1.原理

从MySQL 5.7开始,Oracle MySQL 官方对半同步复制进行了增强,从字面“增强半同步复制”来看,增强半同步复制本质上就是对早期的半同步复制的缺陷进行一些修补增 强,而其原理与半同步复制并无差别。那么,增强半同步复制在早期的半同步复制的基础 上做了什么修改呢?

mysql_semi-syn_rep-4.png

从图中方框标记的地方可以看到,Engine Commit逻辑下沉到了最后,也就是 说,在增强半同步复制下,一个事务在存储引擎内部提交之前,必须要先收到从库的 ACK确认,否则不进行事务最后的提交。这样一来,非发起事务提交的客户端在查询数 据时,所看到的数据就能够和发起事务提交的客户端保持一致,从而解决了在主库故障转 移之后可能出现的幻读问题。

2.开启增强半同步复制

主库配置:

需要提前安装好semisync_master.so

#在主库上启用半同步复制插件
rpl_semi_sync_master_enabled = 1
#在 rpl_semi_sync_master_timeout 超时周期内,如果连接主库的从库数量降到 0 值,主库仍然继续等待从库的接收确认包
rpl_semi_sync_master_wait_no_slave = ON
#主库需要接收到多少个从库的 ACK 确认之后,主库的事务才进行提交。
rpl_semi_sync_master_wait_for_slave_count = 1
#默认值是AFTER_SYNC。主库将每个事务写入binlog中,并将binlog sync 到磁盘。在 sync binlog 之后,主库等待从库该事务的 ack 确认。在收到任意一个从库的确认后,主人将在存储引擎层提交事务,并将事务提交结果返回给客户端
rpl_semi_sync_master_wait_point = AFTER_SYNC

从库配置:

与早期的半同步复制相比,从库无新增参数。需要提前安装好semisync_slave.so。

#在从库上启用半同步复制插件
rpl_semi_sync_slave_enabled = 1