mysql5.7.17 GA版发布,正式推出Group Replication(组复制) 插件,通过这个插件增强了MySQL原有的高可用方案(原有的Replication方案),提供了重要的特性——多写,保证组内高可用,确保数据最终一致性。
1. 背景
在介绍组复制之前,我们先简单介绍传统的异步复制和半同步复制:
1.1 传统复制
传统mysql复制是完全异步化的复制。下图描述了传统复制的原理:
master事务的提交不需要经过slave的确认,slave是否接收到master的binlog,master并不care。slave接收到master binlog后先写relay log,最后异步地去执行relay log中的sql应用到自身。由于master的提交不需要确保slave relay log是否被正确接受,当slave接受master binlog失败或者relay log应用失败,master无法感知。
假设master发生宕机并且binlog还没来得及被slave接收,而切换程序将slave提升为新的master,就会出现数据不一致的情况!另外,在高并发的情况下,传统的主从复制,从节点可能会与主产生较大的延迟(当然mysql后续版本陆续做了优化,推出了并行复制,以此降低异步复制的延迟)。
1.2 半同步复制
基于传统异步存在的缺陷,mysql在5.5版本推出半同步复制。可以说半同步复制是传统异步复制的改进,在master事务的commit之前,必须确保一个slave收到relay log并且响应给master以后,才能进行事务的commit。但是slave对于relay log的应用仍然是异步进行的,原理如下图所示:
因为slave接受relay log之后有可能apply失败。这个时候master其实不知道slave的失败,照常提交了这个事务。并且,半同步复制只确保一个slave能够收到relay log,多slave的场景下,不能保证其他节点正确收到relay log,由此,当发生master切换后,半同步复制一样也会出现数据不一致的情况。
1.3 组复制
基于传统异步复制和半同步复制的缺陷——数据的一致性问题无法保证,MySQL官方在5.7.17版本正式推出组复制(MySQL Group Replication,简称MGR)。
由若干个节点共同组成一个复制组,一个事务的提交,必须经过组内大多数节点(N / 2 + 1)决议并通过,才能得以提交。如上图所示,由3个节点组成一个复制组,Consensus层为一致性协议层,在事务提交过程中,发生组间通讯,由2个节点决议(certify)通过这个事务,事务才能够最终得以提交并响应。
引入组复制,主要是为了解决传统异步复制和半同步复制可能产生数据不一致的问题。组复制依靠分布式一致性协议(Paxos协议的变体),实现了分布式下数据的最终一致性,提供了真正的数据高可用方案(是否真正高可用还有待商榷)。其提供的多写方案,给我们实现多活方案带来了希望。
一个复制组由若干个节点(数据库实例)组成,组内各个节点维护各自的数据副本(Share Nothing),通过一致性协议实现原子消息和全局有序消息,来实现组内实例数据的一致。
2. 组复制介绍
一句话简介:基于分布式一致性协议Paxos实现数据最终一致性的MySQL插件。上面的介绍也已经大概地描述了组复制的相关概念以及它的诞生背景,下面我们关注于它的一些特性:
2.1 数据一致性保证
对于只读(RO)事务,组间实例无需进行通讯,就可以处理事务;但是对于读写(RW)事务,需要经过组内大多数节点决议,来决定该事务是否可以提交。
引用mysql官方博客对于读写事务提交过程的描述,解释了如何保证了组内节点间数据的一致性的:
To be precise, when a transaction is ready to commit at the originating server, the server will atomically broadcasts the write values (rows changed) and the correspondent write set (unique identifiers of the rows that were updated). Then a global total order will be established for that transaction. Ultimately, this means that all servers receive the same set of transactions in the same order. As a consequence, all servers apply the same set of changes in the same order, therefore they remain consistent within the group.
2.2 事务并发冲突处理
在高并发的多写模式(MGR的一种运行模式)下,节点间事务的提交可能会产生冲突,比如,两个不同的事务在两个节点上操作了同一行数据,这个时候就会产生冲突。首先,Group Replication(GR)能够识别到这个冲突,然后对此的处理采用乐观策略:依赖事务提交的时间先后顺序,先发起提交的节点能够正确提交,而后面的提交,会失败。
2.3 节点故障自动检测
GR自带故障检测机制,可以识别组内成员是否挂掉(组内节点心跳检测)。当一个节点失效,将由其他节点决定是否将这个失效的节点从group里面剔除。当然,这是建立在满足大多数节点存活并且可以进行决议的前提上的。
2.4 组成员自动管理
GR自动维护组内节点的状态(在线?存活?挂掉?),对于失效的节点,由其他节点决定是否剔除。对于新加入的节点,GR会自动维护它的视图与其他节点的视图保持一致。关于集群内节点的状态,可以通过performance_schema.replication_group_members
表查看:
举个例子:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
如上有3个节点组成一个GR组,然后状态都是ONLINE(最正常的状态)。可以在组内的3个节点上都看到这个统一的视图。
有些同学可能会问,上面视图中的
MEMBER_HOST
是怎么显示成IP的,其实这个是通过my.cnf配置文件里面指定report_host
而来的,若没有配置report_host
,则MEMBER_PORT将显示为主机名
2.5 容错能力
GR基于分布式一致性算法实现,一个组允许部分节点挂掉,只要保证大多数节点仍然存活并且之间的通讯是没有问题的,那么这个组对外仍然能够提供服务。
假设一个GR由2n + 1
个节点,那么允许n
个节点失效,这个GR仍然能够对外提供服务。比如有3个节点组成的一个GR,可允许1个节点失效,这个GR仍然能够提供服务。
GR组节点数 | 大多数 | 允许挂掉的节点数 |
---|---|---|
1 | 1 | 0 |
2 | 2 | 0 |
3 | 2 | 1 |
4 | 3 | 1 |
5 | 3 | 2 |
6 | 4 | 2 |
7 | 4 | 3 |
【注意】若不满足大多数,所有事务写操作都会阻塞,直到集群满足大多数节点存活为止。
GR建议由奇数个节点组成,若由偶数个节点组成,出现network partition时因为有可能出现对半的情况,两边都没有大多数,因此整个集群实际上会处于不可用。
2.6 两种模式
GR提供了single-primary
和multi-primary
两种模式。single-primary模式下,组内只有一个节点负责写入,读可以从任意一个节点读取,组内数据保持最终一致;而multi-primary模式即为多写方案,即写操作会下发到组内所有节点,组内所有节点同时可读可写,该模式也是能够保证组内数据最终一致性。
注意,一个GR的所有节点必须配置使用同一种模式,不可混用。比如说A、B、C三个节点组成一个GR组,那么要么都运行在single-primary
模式下,要么都运行在multi-primary
模式下。
由
my.cnf
里的配置项group_replication_single_primary_mode
来配置节点到底是运行在single-primary
模式还是multi-primary
模式
2.6.1 Single-Primary Mode
这个模式下,group内只有一台节点可写可读,其他节点只可以读。对于group的部署,需要先跑起primary节点(即那个可写可读的节点,read_only = 0)然后再跑起其他的节点,并把这些节点一一加进group。其他的节点就会自动同步primary节点上面的变化,然后将自己设置为只读模式(read_only = 1)。
当primary节点意外宕机或者下线,在满足大多数节点存活的情况下,group内部发起选举,选出下一个可用的读节点,提升为primary节点。
primary选举根据group内剩下存活节点的server_uuid
按字典序升序来选择,即剩余存活的节点按server_uuid字典序排列,然后选择排在最前的节点作为新的primary节点。
引用 MySQL 5.7 Reference Manual :
Upon primary member failure, an automatic leader election mechanism chooses the next primary member. The next primary is selected by ordering the remaining servers lexicographically (using their UUID) and picking the first member in the list.
【特别重要】 在切换primary期间,mysql group不会处理应用重连接到新的主,这需要应用层自己或者由另外的中间件层(proxy or router)去保证。
如何查看group内哪个节点是作为primary节点,官方提供了一个方法:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
得到的是实例节点的server_uuid。利用上面的SQL,加上performance_schema
里的replication_group_members
表,可以查出更细节的信息,包括hostname,port等,sql语句如下所示:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
另外,还可以通过以下SQL来直接判断当前节点是否为主节点,得到1表示主节点,0表示不是主节点:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
最后发现一个便捷的方式,可以通过直接查看read_only
变量来判断节点是否为主节点:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
2.6.2 Multi-Primary Mode
多主模式,即多写,没有选择新primary的概念(无需进行选举),group内的所有机器都是primary节点,同时可以进行读写操作,并且数据是最终一致的。但是,这个模式下仍然存在一些使用限制,限制请看2.7.2小节介绍。
2.7 约束与限制
2.7.1 约束
部署GR有以下需求:
1) 架构上
- 存储引擎必须为innodb
- 每个表必须提供主键
- 只支持ipv4,网络带宽要好
- 一个group最多只能有9个节点
2) 配置上
针对my.cnf,需要指定如下配置:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
2.7.2 限制
以下是使用GR的限制:
- 不支持Replication event checksums,需要在my.cnf里面配置,在上节已经提及
- 不支持Savepoints
- multi-primary mode部署方式不支持SERIALIZABLE事务隔离级别
- multi-primary mode部署方式不能完全支持级联外键约束
- multi-primary mode部署方式不支持在不同节点上对同一个数据库对象并发执行DDL(在不同节点上对同一行并发进行RW事务,后发起的事务会失败)