一、MySQL复制的三种模式
MySQL当前存在的三种复制模式有:异步模式、半同步模式和组复制模式,先了解一下三种模式的工作方式。
1、MySQL Asynchronous Replication(异步复制)
异步复制是MySQL最早的也是当前使用最多的复制模式,异步复制提供了一种简单的主-从复制方法,包含一个主库(master)和备库(一个,或者多个)之间,主库执行并提交了事务,在这之后(因此才称之为异步),这些事务才在从库上重新执行一遍(基于statement)或者变更数据内容(基于row),主库不检测其从库上的同步情况。在服务器负载高、服务压力大的情况下主从产生延迟一直是其诟病。工作流程简图如下:
2、MySQL Semisynchronous Replication(半同步复制)
MySQL5.5的版本在一步同步的基础之上,以插件的形式实现了一个变种的同步方案,称之为半同步(semi-sync
replication)。这个插件在源生的异步复制上,添加了一个同步的过程:当从库接收到了主库的变更(即事务)时,会通知主库。主库上的操作有两种:接收到这个通知以后才去commit事务;接受到之后释放session。这两种方式是由主库上的具体配置决定的。当主库收不到从库的变更通知超时时,由半同步复制自动切换到异步同步,这样就极大了保证了数据的一致性(至少一个从库),但是在性能上有所下降,特别是在网络不稳定的情况下,半同步和同步之间来回切换,对正常的业务是有影响的。其工作流程简图如下:
3、Group Replication(组复制)
不论是异步复制还是半同步复制,都是一个主下面一个从或是多个从的模式,在高并发下高负载下,都存在延迟情况,此时如果主节点出现异常,那么就会出现数据不一致的情况,数据可能会丢,在金融级数据库中是不能容忍的。在这种情况下,急需出现一种模式来解决这些问题。在MySQL5.7.17的版本中,带着这些期待,新的复制模式组复制产生并GA了(本文的测试等数据均基于MySQL5.7.17)。
组复制的工作流程图如下:
二、组复制的工作原理
MySQL组复制是一个MySQL插件,它建立在现有的MySQL复制基础结构上,利用了二进制日志,基于行的日志记录和全局事务标识符等功能。它集成了当前的MySQL框架,如性能模式、插件和服务基础设施等。
组复制(Group
Replication)基于分布式一致性算法(Paxos协议的变体)实现,一个组允许部分节点挂掉,只要保证绝大多数节点仍然存活并且之间的通讯是没有问题的,那么这个组对外仍然能够提供服务,它是一种被使用在容错系统中的技术。Group
Replication(复制组)是由能够相互通信的多个服务器(节点)组成的。在通信层,Group
replication实现了一系列的机制:比如原子消息(atomic message delivery)和全序化消息(total ordering of
messages)。这些原子化,抽象化的机制,为实现更先进的数据库复制方案提供了强有力的支持。MySQL Group
Replication正是基于这些技术和概念,实现了一种多主全更新的复制协议。简而言之,一个Group
Replication就是一组节点,每个节点都可以独立执行事务,而读写事务则会在于group内的其他节点进行协调之后再commit。因此,当一个事务准备提交时,会自动在group内进行原子性的广播,告知其他节点变更了什么内容/执行了什么事务。这种原子广播的方式,使得这个事务在每一个节点上都保持着同样顺序。这意味着每一个节点都以同样的顺序,接收到了同样的事务日志,所以每一个节点以同样的顺序重演了这些事务日志,最终整个group保持了完全一致的状态。然而,不同的节点上执行的事务之间有可能存在资源争用。这种现象容易出现在两个不同的并发事务上。假设在不同的节点上有两个并发事务,更新了同一行数据,那么就会发生资源争用。面对这种情况,Group
Replication判定先提交的事务为有效事务,会在整个group里面重放,后提交的事务会直接中断,或者回滚,最后丢弃掉。因此,这也是一个无共享的复制方案,每一个节点都保存了完整的数据副本。
从其工作的原理可以看出,Group
Replication基于Paxos协议的一致性算法校验事务执行是否有冲突,然后顺序执行事务,达到最终的数据一致性,也就意味着部分节点可以存在延迟。可以设置多主同时写入和单主写入,通过设置group_replication_single_primary_mode来进行控制是多主还是单主,官方推荐单主写入,允许延迟,但延迟过大,则会触发限流规则(可配置的),整个集群会变的很慢,性能大打折扣。
三、组复制的程序结构
在MySQL的底层,GR增加了另外的API层来实现所需要的功能。程序结构上,GRAPI主要分为三部分:
1:capture 追踪当前正在执行的事务的上下文。
2:applier 执行远程事务传输到本地的日志到本地数据库。
3:recovery 负责分布式环境下的节点恢复,以及相关的数据回追,失败处理等。
在这几个主要API层的下面,是统一的复制协议逻辑处理层,这一层主要是统一应用层的各种调用。在更下层,则是通用程度更高的分布式通讯层,处于调用便利,分布式通讯曾对上提供使用的API,API的下面,是Paxos实现的分布式通讯协议组件,这个组件与集群中其他节点一起,形成一个虚拟概念化的分布式集群。
四、消息压缩(Message Compression)
这个压缩主要是指MySQL的bin-log压缩,所使用的压缩算法是LZ4。当网络带宽是瓶颈时,消息压缩可以在组通信级别提供高达30-40%的吞吐量改进,这在网络传输压力比较大的组中是尤为重要的。LZ4能很好的支持多线程环境,获得更高的压缩和解压速度。以下是压缩算法LZ4的压缩和解压情况:
压缩发生在组通信引擎级别,之前数据被交给组通信线程,所以它发生在mysql用户会话线程的上下文中。事务有效网络负载可以在被发送到组之前被压缩,并且在被接收时被解压缩。压缩是有条件的,并且取决于配置的阈值。默认情况下启用压缩,此外,它并不要求组中的所有服务器节点都启用压缩机制,在接收到消息时,成员检查消息信封以验证它是否被压缩,如果需要,则成员解压缩该事务,然后将其传递到上层。
默认情况下启用压缩,阈值为1000000字节(1MB)。压缩阈值(以字节为单位)可以设置为大于默认值。在这种情况下,只有具有大于阈值的有效负载的事务被压缩。下面是如何设置压缩阈值的示例。
STOP GROUP_REPLICATION; SET GLOBAL group_replication_compression_threshold= 2097152; START GROUP_REPLICATION;
这将压缩阈值设置为2MB。如果事务生成的有效内容大于2MB的复制消息,例如大于2MB的二进制日志事务条目,则会对其进行压缩。禁用压缩设置阈值为0。注意:修改这个阈值是需要重启组复制的。
消息压缩流程图如下:
五、组复制的要求和限制
1、限制和要求
1. 所有涉及的数据都必须发生在InnoDB存储引擎的表内。
2. 所有的表必须有明确的主键定义。
3. 网络地址只支持IPv4。
4. 需要低延迟,高带宽的网络。
5. 目前集群限制最多允许9个节点。
6. 必须启用binlog。
7. binlog 格式必须是row格式。
8. 必须打开gtid模式。
9. 复制相关信息必须使用表存储。
10.事务写集合(Transaction write set
extraction)必须打开。(这个目前与savepoint冲突,这也是导致mysqldump无法备份GR实例的原因)
11. log slave updates必须打开。
12. binlog的checksum目前不支持。
13. 由于事务写集合的干扰,无法使用savepoint。
14. SERIALIZABLE 隔离级别目前不支持。
15.
对同一个对象,在集群中不同的实例上,并行地执行DDL(哪怕是相互冲突的DDL)是可行的,但会导致数据一致性等方面的错误,目前阶段不支持在多节点同时执行同一对象的DDL。
16. 外键的级联约束操作目前的实现并不完全支持,不推荐使用。
2、组复制的相关配置
依据组复制的要求和限制,以下设置根据MySQL组复制要求配置复制:
server_id = 1
gtid_mode = ON
enforce_gtid_consistency = ON
master_info_repository = TABLE
relay_log_info_repository = TABLE
binlog_checksum = NONE
log_slave_updates = ON
log_bin = binlog
binlog_format = ROW
此时my.cnf文件可确保服务器配置,并指示实例化一个给定的配置下的复制基础设施。以下部分配置服务器的组复制设置。具体参数比较简单,不在这里赘述,可参见官方说明:
transaction_write_set_extraction = XXHASH64
loose-group_replication_group_name =“aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa”
loose-group_replication_start_on_boot = off
loose-group_replication_local_address ="127.0.0.1:24901”
loose-group_replication_group_seeds =“127.0.0.1:24901,127.0.0.1:24902,127.0.0.1:24903”
loose-group_replication_bootstrap_group = off
具体的组复制安装部署比较简单,网上和官方说明都有说明,在这里就不阐述安装部署这块了。
六、组复制的多主和单主模式(Multi-Primary or Single-Primary Mode)
组复制分为多主和单主两种模式,默认是单主模式,也是官方推荐的组复制模式。单个集群中不能同时使用两种模式,例如一个配置在多主模式,而另一个在单主模式。要在模式之间切换,需要使用不同的操作配置重新启动集群。无论部署模式如何,组复制不处理客户端故障切换,它必须由应用程序本身、连接器或中间件框架(如代理或路由器)等处理。
1、单主模式
在此模式下,组具有设置为读写模式的单主实例,主节点通常是用于解析组的第一个服务器,组中的其他其他节点都自动设置为只读模式(即,超级只读),所有其他加入的节点自动识别主节点并设置为自己为只读。
在单主机模式下,将禁用在多主机模式下部署的某些检查,因为系统会强制每次只有一个写入节点。例如,允许对具有级联外键的表进行更改,而在多主模式下不允许。在主节点故障时,自动选举机制选择下一个主节点。通过按字典顺序(使用其UUID)并选择列表中的第一个节点来排序剩余的节点来选择下一个主节点。如果主节点从组中删除,则执行选择,并从组中的其余节点中选择新的主节点,这个选择按照词典顺序排序节点UUID并选择第一个来执行。一旦选择了新的主节点,其他节点将设置为从节点,从节点为只读。如下图:
2、多主模式
在多主模式下,没有单个主模式的概念,也没有选举程序,因为没有节点发挥任何特殊的作用。加入组时,所有服务器都设置为读写模式。
在多主要模式下部署时,将检查语句以确保它们与模式兼容。在以多主模式部署组复制时进行以下检查:
1:如果事务在SERIALIZABLE隔离级别下执行,则在将其与组同步时,它的提交将失败。
2:如果事务对具有级联约束的外键执行,则事务在与组同步时无法提交。
这些检查可通过设置选项停用 group_replication_enforce_update_everywhere_checks
到FALSE。当在单主要方式部署,该选项必须设置为 FALSE。如下图:
七、运维相关问题
1、故障切换问题
目前MySQL官方没有发布连接组复制专用的客户端(如MongoDB连接复制集的客户端),在实际的应用中如果发生故障,需要客户端自己来处理。对于单主模式的话,如果主节点发生故障,客户端需要判断新的主节点是谁,然后把写切换到新的主节点,基本上和当前的异步同步的主从切换一样,并且新的主节点是集群自动产生,不可控;多主模式需要在客户端进行节点可用性检查,当其中的一个写节点不可用时自动使用其他可用节点。
在实际生产中,综合两种组复制模式的故障切换,可以使用多主模式,指定其中一个节点为主节点,其他节点置为只读节点,这样主节点故障时,新的主节点可控。
2、大事务支持问题
目前版本测试并发进行大数据操作和DDL操作时,kill掉大事务,有几率造成集群不可用;在insert into
…….select……limit……这种大事务支持不好,可能造成集群不用;多主模式进行DDL操作需要集群内所有节点都为ONLINE状态才可执行,处于ERROR和RECOVERING状态时有几率导致集群堵塞,严重时集群不可用。
3、备份问题
在组复制集群其中的一个节点上执行数据库备份时,不管使用mysqldump(这个不能使用--single-transaction参数,生产中不建议使用mysqldump备份集群数据)或是使用xtrabackup的QPS下降40%,并且备份节点基本停止读写。在测试备份文件导入数据时,多主模式要比单主模式慢。推荐使用组复制+异步复制方式,在异步复制的从节点上进行数据库备份。
4、二进制日志删除问题
因为组复制同步还是基于二进制日志来进行同步的,清理某个节点bin-log时,必须判定这个日志文件是否还在使用,如果在使用,则绝对不能删除,如果删除,则整个集群直接ERROR。
5、同步延迟问题
目前MySQL5.7.17的版本中无法直观查看节点同步延迟,也无法获取延迟多少,不管是时间或事物数,这个打开MySQL的Debug模式,可以获取到节点的延迟事务情况。
组复制的延迟对集群是有影响的,一旦出现延迟(默认延迟25000个事务),则启动流量控制(Flow
Control),每个周期性能衰减当前的10%,直到集群不可用(但集群节点状态为online),单个节点慢整个集群全慢。
集群中的每个节点都会验证并应用该组提交的事务,有关校验和应用程序过程的统计信息对于了解应用程序队列如何增长,已找到多少冲突,检查了多少事务,在哪里提交了哪些事务等等非常有用。表
performance_schema.replication_group_member_stats
提供与事务认证过程的相关信息,但没有延迟信息。相关字段解释如下:
字段 |
描述 |
Channel_name |
组复制通道的名称。 |
Member_ID |
代表当前连接到的成员服务器UUID。组中的每个节点具有不同的值,是一个唯一键,因为它对每个成员是唯一的。 |
Count_Transactions_in_queue |
队列中等待冲突检测检查的事务数。一旦检查到冲突,并且他们通过检查,他们将排队等待应用。 |
Count_transactions_checked |
表示已检查冲突的事务数。 |
Count_conflicts_detected |
表示未通过冲突检测检查的事务数。 |
Count_transactions_validating |
表示冲突检测数据库的当前大小(每个事务经过验证的数据库)。 |
Transactions_committed_all_members |
表示已在当前视图的所有成员上成功提交的事务。这以固定的时间间隔更新。 |
Last_conflict_free_transaction |
显示最后一次无冲突校验检查的事务标识符。 |
6、数据一致性问题
不管是多写还是单写,都并非是强一致性,均允许有延迟,他在校验完事务是否冲突后把当前广播到各个节点并确定各个节点收到事务后即进入下一个事物的冲突检测,此时每个节点只是拿到了所有事务的执行序列,保证了事务最终顺序执行,从而保证数据的最终一致性,但同一时刻并非强一致性的。
7、节点故障脑裂问题
节点越多性能损耗越大,三个节点比较合适。节点故障可能有脑裂等问题:如5个节点分布在两个机房,机房间网络断掉分为两个部分,2个集群的机房不可用,3个节点的可用,而三个节点的机房网络有问题,此时如果想使两个节点的机房可用,需要重新对两个节点做集群重组,三个节点的就无法恢复到两个节点中去;三节点中其中一个节点宕机,其他两个正常节点可用,故障节点重启没有加入到集群时,此时这个节点以单实例存在可读写,此时会发生脑裂。
8、网络延迟问题
测试过程中使用TC命令来模拟网络延迟:
tc qdisc add dev eth0 root netem delay 50ms 10ms 增加网络延迟50ms,10ms左右的浮动
tc qdisc del dev eth0 root netem delay 50ms 10ms 删除网络延迟
经过测试网络延迟对比组复制MySQL的QPS:网络延迟设置50ms和正常的对比,QPS降低至少1/3,甚至1/2,网络延迟对性能影响挺大。以下是测试情况:
9、弹性扩展问题
MySQL官方网站提到了组复制的弹性自动扩展,经过实际测试,这种扩展在生产中是不现实的。可用于生产的弹性扩展要求新加入一个集群,集群中的数据完全由集群来完成自动同步,但由于组复制是基于二进制日志来进行同步的,生产中是不可能完整保留全部的二进制日志,在新加入的节点需要先备份出集群的全量数据,然后根据同步位置去追事务达到数据的一致后节点状态online状态,其实和之前异步同步搭建主从一样。并且官方提示如果恢复时的延迟过大,可能也无法正常达到追到最新数据的位置。
10、客户端连接问题
官方说明中关于故障处理的时候有一句话:组复制不处理客户端故障切换,它必须由应用程序本身,连接器或中间件框架(如代理或路由器)处理。官方一再强调MySQL组复制提供了高可用、高弹性、可靠的MySQL服务,那么官方是否提供一套类似MongoDB复制集的客户端组件来支持那?
目前的解决方法就是和异步复制的切换差不多,使用域名切换或是自己实现一套高可用的客户端连接方式。但就目前来说效率最高的是结合自己的业务,修改组复制故障处理的源码,当检测到写节点故障时结合自己的域名切换来处理。但这样对DBA来说需要源码开发能力,相对要求比较高。
11、查找主节点IP问题
在单主模式下,不能直观的获取主库的IP地址,使用以下命令可以获取到主节点的UUID:
mysql> SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE
VARIABLE_NAME =‘group_replication_primary_member‘;
+ -------------------------------------- +
| VARIABLE_VALUE |
+ -------------------------------------- +
| 69e1a3b8-8397-11e6-8e67-bf68cbc061a4 |
+ -------------------------------------- +
1行(0,00秒)
使用SELECT * FROM
performance_schema.replication_group_members可以查看到UUID对应到的MEMBER_HOST,而MEMBER_HOST指的是主机名,需要在MySQL的配置文件中指定report-host为IP地址,这样就可以两个表关联查询到主库的IP地址。
八、总结
从测试的情况来看,对大事务等的支持不够,运维管理方面做的不友好,相关组复制的配套监控、客户端等不完善,有一部分问题是可以规避和曲线解决的,有一部分需要源码层面的支持;在性能上和PXC对比,要优于PXC,这个和各自的复制协议不同分不开的。
MySQL组复制提供了高可用、高弹性、可靠的MySQL服务,旨在打造金融级MySQL集群。在忽略网络延迟的情况,可以轻松的实现多活和异地调用就近写库,这一点是业务上比较期待的。组复制是MySQL未来的一个发展趋势,相信在未来的版本中会更加的完善,期待成熟版本。
参考文档:http://dev.mysql.com/doc/refman/5.7/en/group-replication.html