MySQL当前存在的三种复制模式有:异步模式、半同步模式和组复制模式,先了解一下三种模式的工作方式。 1、MySQL Asynchronous Replication(异步复制)异步复制是MySQL最早的也是当前使用最多的复制模式,异步复制提供了一种简单的主-从复制方法,包含一个主库(master)和备库(一个,或者多个)之间,主库执行并提交了事务,在这之后(因此才称之为异步),这些事务才在从库上重新执行一遍(基于statement)或者变更数据内容(基于row),主库不检测其从库上的同步情况。在服务器负载高、服务压力大的情况下主从产生延迟一直是其诟病。工作流程简图如下: 2、MySQL Semisynchronous Replication(半同步复制)MySQL5.5的版本在一步同步的基础之上,以插件的形式实现了一个变种的同步方案,称之为半同步(semi-sync replication)。这个插件在源生的异步复制上,添加了一个同步的过程:当从库接收到了主库的变更(即事务)时,会通知主库。主库上的操作有两种:接收到这个通知以后才去commit事务;接受到之后释放session。这两种方式是由主库上的具体配置决定的。当主库收不到从库的变更通知超时时,由半同步复制自动切换到异步同步,这样就极大了保证了数据的一致性(至少一个从库),但是在性能上有所下降,特别是在网络不稳定的情况下,半同步和同步之间来回切换,对正常的业务是有影响的。其工作流程简图如下: 3、GroupReplication(组复制)不论是异步复制还是半同步复制,都是一个主下面一个从或是多个从的模式,在高并发下高负载下,都存在延迟情况,此时如果主节点出现异常,那么就会出现数据不一致的情况,数据可能会丢,在金融级数据库中是不能容忍的。在这种情况下,急需出现一种模式来解决这些问题。在MySQL5.7.17的版本中,带着这些期待,新的复制模式组复制产生并GA了(本文的测试等数据均基于MySQL5.7.17)。 组复制的工作流程图如下: MySQL组复制是一个MySQL插件,它建立在现有的MySQL复制基础结构上,利用了二进制日志,基于行的日志记录和全局事务标识符等功能。它集成了当前的MySQL框架,如性能模式、插件和服务基础设施等。 组复制(Group Replication)基于分布式一致性算法(Paxos协议的变体)实现,一个组允许部分节点挂掉,只要保证绝大多数节点仍然存活并且之间的通讯是没有问题的,那么这个组对外仍然能够提供服务,它是一种被使用在容错系统中的技术。Group Replication(复制组)是由能够相互通信的多个服务器(节点)组成的。在通信层,Group replication实现了一系列的机制:比如原子消息(atomic message delivery)和全序化消息(totalordering 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主要分为三部分:
|
字段 |
描述 |
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 |
显示最后一次无冲突校验检查的事务标识符。 |
不管是多写还是单写,都并非是强一致性,均允许有延迟,他在校验完事务是否冲突后把当前广播到各个节点并确定各个节点收到事务后即进入下一个事物的冲突检测,此时每个节点只是拿到了所有事务的执行序列,保证了事务最终顺序执行,从而保证数据的最终一致性,但同一时刻并非强一致性的。
节点越多性能损耗越大,三个节点比较合适。节点故障可能有脑裂等问题:如5个节点分布在两个机房,机房间网络断掉分为两个部分,2个集群的机房不可用,3个节点的可用,而三个节点的机房网络有问题,此时如果想使两个节点的机房可用,需要重新对两个节点做集群重组,三个节点的就无法恢复到两个节点中去;三节点中其中一个节点宕机,其他两个正常节点可用,故障节点重启没有加入到集群时,此时这个节点以单实例存在可读写,此时会发生脑裂。
测试过程中使用TC命令来模拟网络延迟:
tcqdiscadddeveth0rootnetemdelay50ms10ms增加网络延迟50ms,10ms左右的浮动
tcqdiscdeldeveth0rootnetemdelay50ms10ms删除网络延迟
经过测试网络延迟对比组复制MySQL的QPS:网络延迟设置50ms和正常的对比,QPS降低至少1/3,甚至1/2,网络延迟对性能影响挺大。以下是测试情况:
MySQL官方网站提到了组复制的弹性自动扩展,经过实际测试,这种扩展在生产中是不现实的。可用于生产的弹性扩展要求新加入一个集群,集群中的数据完全由集群来完成自动同步,但由于组复制是基于二进制日志来进行同步的,生产中是不可能完整保留全部的二进制日志,在新加入的节点需要先备份出集群的全量数据,然后根据同步位置去追事务达到数据的一致后节点状态online状态,其实和之前异步同步搭建主从一样。并且官方提示如果恢复时的延迟过大,可能也无法正常达到追到最新数据的位置。
官方说明中关于故障处理的时候有一句话:组复制不处理客户端故障切换,它必须由应用程序本身,连接器或中间件框架(如代理或路由器)处理。官方一再强调MySQL组复制提供了高可用、高弹性、可靠的MySQL服务,那么官方是否提供一套类似MongoDB复制集的客户端组件来支持那?
目前的解决方法就是和异步复制的切换差不多,使用域名切换或是自己实现一套高可用的客户端连接方式。但就目前来说效率最高的是结合自己的业务,修改组复制故障处理的源码,当检测到写节点故障时结合自己的域名切换来处理。但这样对DBA来说需要源码开发能力,相对要求比较高。
在单主模式下,不能直观的获取主库的IP地址,使用以下命令可以获取到主节点的UUID:
mysql