复制架构衍生史

在谈这个特性之前,我们先来看看MySQL的复制架构衍生史。MySQL的复制分为四种:

1、普通的replication,异步。

搭建简单,使用非常广泛,从MySQL诞生之初,就产生了这种架构,性能非常好,可谓非常成熟。但是这种架构数据是异步的,所以有丢失数据库的风险。

2、semi-sync replication,半同步。

性能,功能都介于异步和全同步中间。从mysql5.5开始诞生,目的是为了折中上述两种架构的性能以及优缺点。

3、sync replication,同步。

目前官方MySQL 5.7.17基于Group replication的全同步技术已经问世,全同步技术带来了更多的数据一致性保障。相信是未来同步技术一个重要方向,值得期待。MySQL 5.7 Group Replication

4、mysql cluster,同步。

基于NDB引擎,搭建也简单,本身也比较稳定,是MySQL里面对数据保护最靠谱的架构,也是目前唯一一个数据完全同步的架构,数据零丢失。不过对业务比较挑剔,限制也较多。MySQL NDB CLUSTER

根据这几种复制协议,分别对应MySQL几种复制类型,分别是异步、半同步、同步。

MySQL 5.7半同步复制技术

半同步复制技术

我们今天谈论第二种架构。我们知道,普通的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批量事务,导致很大的延迟。

为了弥补以上几种场景的不足,MySQL从5.5开始推出了半同步复制。即在master的dumper线程通知slave后,增加了一个ack(消息确认),即是否成功收到t1的标志码,也就是dumper线程除了发送t1到slave,还承担了接收slave的ack工作。如果出现异常,没有收到ack,那么将自动降级为普通的复制,直到异常修复。

本同步复制原理图:

MySQL 5.7半同步复制技术

master将每个事务写入binlog,传递到slave刷新到磁盘(relay log),同时主库提交事务。master等待slave反馈收到relay log,只有收到ACK后master才将commit OK结果反馈给客户端。

半同步复制的潜在问题

1)如果异常发生,会降级为普通的复制,那么从机出现数据不一致的几率会减少,并不是完全消失。

2)主机dumper线程承担的工作变多了,这样显然会降低整个数据库的性能。

3)在MySQL 5.5~5.6使用after_commit的模式下,客户端事务在存储引擎层提交后,再得到从库确认的过程中,主库宕机了,此时,可能的情况有两种都会导致数据不一致。

3.1 事务还没发送到从库上

此时,客户端会收到事务提交失败的信息,客户端会重新提交该事务到新的主上,当宕机的主库重新启动后,以从库的身份重新加入到该主从结构中,会发现,该事务在从库中被提交了两次,一次是之前作为主的时候,一次是被新主同步过来的,数据出现不一致情况。

3.2 事务已经发送到从库上

此时,从库已经收到并应用了该事务,但是客户端仍然会收到事务提交失败的信息,重新提交该事务到新的主上,数据出现不一致情况。

MySQL5.6半同步复制配置

具体完整配置可参考:MySQL基于日志点做主从复制(二)

Master配置

1)安装半同步模块并启动(此模块就在/usr/local/mysql/lib/plugin/semisync_master.so)

1

Mysql> install plugin rpl_semi_sync_master soname 'semisync_master.so';

MySQL 5.7半同步复制技术

1

2

Mysql> set global rpl_semi_sync_master_enabled = 1;

Mysql> set global rpl_semi_sync_master_timeout = 2000;

安装后启动和定制主从连接错误的超时时间默认是10s可改为2s,一旦有一次超时自动降级为异步。(以上内容要想永久有效需要写到配置文件中)

1

2

3

4

[root@localhost ~]# cat /etc/my.cnf

[mysqld]

rpl_semi_sync_master_enabled = 1;

rpl_semi_sync_master_timeout = 2000;

Slave配置

1)安装半同步模块并启动

1

2

3

4

5

6

7

8

9

mysql> install plugin rpl_semi_sync_slave soname 'semisync_slave.so';

Mysql> set global rpl_semi_sync_slave_enabled = 1;

mysql> show global variables like '%semi%'\G;

*************************** 1. row ***************************

Variable_name: rpl_semi_sync_slave_enabled

Value: ON

*************************** 2. row ***************************

Variable_name: rpl_semi_sync_slave_trace_level

Value: 32

2)从节点需要重新连接主服务器半同步才会生效

1

2

Mysql> stop slave io_thread;

Mysql> start slave io_thread;

PS:如果想卸载异步模块就使用uninstall即可。

Master上查看是否启用了半同步

MySQL 5.7半同步复制技术

现在半同步已经正常工作了,那么可以验证一下半同步超时,是否会自动降为异步工作。可以在Slave上停掉半同步协议,然后在Master上创建数据库看一下能不能复制到Slave上。

Slave

1

2

3

4

# 关闭半同步;

Mysql> set global rpl_semi_sync_slave_enabled = 0 ;

Mysql> stop slave io_thread;

Mysql> start slave io_thread;

Master

1

2

3

4

mysql> create database dbtest;

Query OK, 1 row affected (2.01 sec)

mysql> create database dbtest01;

Query OK, 1 row affected (0.01 sec)

创建第一个数据库花了2.01秒,而我们前面设置的超时时间是2秒,而创建第二个数据库花了0.01秒,由此得出结论是超时转换为异步传送。

随着MySQL 5.7版本的发布,半同步复制技术升级为全新的Loss-less Semi-Synchronous Replication架构,其成熟度、数据一致性与执行效率得到显著的提升。

MySQL 5.7半同步数据复制效率的改进

MySQL5.7安装半同步模块

1

2

mysql> install plugin rpl_semi_sync_master soname 'semisync_master.so';

Query OK, 0 rows affected (0.00 sec)


1

2

3

4

5

6

7

8

9

10

11

12

mysql> show global variables like '%semi%';

+-------------------------------------------+------------+

| 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 |

+-------------------------------------------+------------+

6 rows in set (0.00 sec)

1)支持无损复制

在无损复制中,master把binlog发送给slave,只有在slave把binlog写到本地的relay-log里,master才会将事务提交到存储引擎层,然后把请求返回给客户端,客户端才可以看见刚才提交的事务。在一个事物提交的过程中,在MySQL Server层的binlog阶段后,Master节点需要收到至少一个Slave节点回复的ACK后,才能继续下一个事物。

半同步复制与无损复制的对比

1.1 ACK的时间点不同

  • 半同步复制在InnoDB层的Commit Log后,等待ACK。

  • 无损复制在MySQL Server层的Write binlog后,等待ACK。

1.2 主从数据一致性

  • 半同步复制意味着在Master节点上,这个刚刚提交的事物对数据库的修改,对其他事物是可见的。

  • 无损复制在write binlog完成后,就传输binlog,但还没有去写commit log,意味着当前这个事物对数据库的修改,其他事物也是不可见的。

无损复制其实就是对semi sync增加了rpl_semi_sync_master_wait_point参数,来控制半同步模式下主库在返回给会话事务成功之前提交事务的方式。rpl_semi_sync_master_wait_point该参数有两个值:

第一个值:AFTER_COMMIT(5.6默认值)

master将每个事务写入binlog,传递到slave刷新到磁盘(relay log),同时主库提交事务。master等待slave反馈收到relay log,只有收到ACK后master才将commit OK结果反馈给客户端。

MySQL 5.7半同步复制技术

第二个值:AFTER_SYNC(5.7默认值,但5.6中无此模式)

master将每个事务写入binlog , 传递到slave刷新到磁盘(relay log)。master等待slave反馈接收到relay log的ack之后,再提交事务并且返回commit OK结果给客户端。 即使主库crash,所有在主库上已经提交的事务都能保证已经同步到slave的relay log中。

MySQL 5.7半同步复制技术

因此5.7引入了无损复制(after_sync)模式,带来的主要收益是解决after_commit导致的master crash主从间数据不一致问题,因此在引入after_sync模式后,所有提交的数据已经都被复制,故障切换时数据一致性将得到提升。

2)性能提升,支持发送binlog和接受ack的异步化。

旧版本的semi sync受限于dump thread ,原因是dump thread承担了两份不同且又十分频繁的任务:传送binlog给slave ,还需要等待slave反馈信息,而且这两个任务是串行的,dump thread必须等待slave返回之后才会传送下一个events事务。dump thread已然成为整个半同步提高性能的瓶颈。在高并发业务场景下,这样的机制会影响数据库整体的TPS 。

MySQL 5.7半同步复制技术

为了解决上述问题,在5.7版本的semi sync框架中,独立出一个Ack Receiver线程 ,专门用于接收slave返回的ack请求,这将之前dump线程的发送和接受工作分为了两个线程来处理。这样master上有两个线程独立工作,可以同时发送binlog到slave,和接收slave的ack信息。因此半同步复制得到了极大的性能提升。这也是MySQL 5.7发布时号称的Faster semi-sync replication。

MySQL 5.7半同步复制技术

但是在MySQL 5.7.17之前,这个Ack Receiver线程采用了select机制来监听slave返回的结果,然而select机制监控的文件句柄只能是0-1024,当超过1024时,用户在MySQL的错误日志中或许会收到类似如下的报错,更有甚者会导致MySQL发生宕机。

semi-sync master failed on net_flush() before waiting for slave reply.

MySQL 5.7.17版本开始,官方修复了这个bug,开始使用poll机制来替换原来的select机制,从而可以避免上面的问题。其实poll调用本质上和select没有区别,只是在I/O句柄数理论上没有上限了,原因是它是基于链表来存储的。但是同样有缺点:比如大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。

其实在高性能软件中都是用另外一种调用机制,名为epoll,高性能的代表,比如Nginx,haproxy等都是使用epoll。可能poll的复杂性比epoll低,另外对于ack receiver线程来说可能poll足矣。

3)性能提升,控制主库接收slave写事务成功反馈数量。

MySQL 5.7新增了rpl_semi_sync_master_wait_slave_count参数,可以用来控制主库接受多少个slave写事务成功反馈,给高可用架构切换提供了灵活性。如图所示,当count值为2时,master需等待两个slave的ack。

MySQL 5.7半同步复制技术

4)性能提升, Binlog互斥锁改进。

旧版本半同步复制在主提交binlog的写会话和dump thread读binlog的操作都会对binlog添加互斥锁,导致binlog文件的读写是串行化的,存在并发度的问题。

MySQL 5.7半同步复制技术

MySQL 5.7对binlog lock进行了以下两方面优化:

1. 移除了dump thread对binlog的互斥锁。

2. 加入了安全边际保证binlog的读安全。

MySQL 5.7半同步复制技术