MySQL组复制系列文章:php
MySQL的组复制能够配置为单主模型和多主模型两种工做模式,它们都能保证MySQL的高可用。如下是两种工做模式的特性简介:html
虽然多主模型的特性很诱人,但缺点是要配置和维护这种模式,必需要深刻理解组复制的理论,更重要的是,多主模型限制较多,其一致性、安全性还须要多作测试。mysql
而使用单主模型的组复制就简单的太多了,惟一须要知道的就是它会自动选举master节点这个特性,由于它的维护一切都是自动进行的,甚至对于管理人员来讲,彻底能够不用去了解组复制的理论。算法
虽然单主模型比多主模型的性能要差,但它没有数据不一致的危险,加上限制少,配置简单,基本上没有额外的学习成本,因此多数状况下都是配置单主模型的组复制,即便是PXC和MariaDB也如此。sql
虽然说组复制的单主模型很简单,但有必要了解一点和单主模型有关的理论,尽管不了解也没什么问题,毕竟一切都是自动的。shell
以下图,master节点为s1,其他为slave节点。数据库
组复制一切正常时,全部的写操做都路由到s1节点上,全部的读操做都路由到s二、s三、s4或s5上。当s1节点故障后,组复制自动选举新的master节点。假如选举s2为新master成功后,s三、s4和s5将指向s2,写操做将路由到s2节点上。bootstrap
至于如何改变客户端的路由目标,这不是组复制应该考虑的事情,而是客户端应用程序应该考虑的事情。实际上,更好的方式是使用中间件来作数据库的路由,好比MySQL Router、ProxySQL、amoeba、cobar、mycat。centos
上面一直说,单主模型是自动选举主节点的,那么如何选举?安全
首先,在第一个MySQL节点s1启动时,通常会将其设置为组的引导节点,所谓引导就是在启动组复制功能时去建立一个复制组。固然,这并不是强制要求,也能够设置第二个启动节点做为组的引导节点。由于组内没有其余节点,因此这第一个节点会直接选为master节点。
而后,若是有第二个节点要加入组时,新节点须要征得组的赞成,由于目前只有一个节点,因此只需s1节点赞成便可。新节点在加入组时,首先会联系s1,与s1创建异步复制的通道,并从s1节点处获取s2上目前缺失的数据,等到s1和s2节点上的数据同步后,s2节点就会真正成为组中的新成员。固然,实际过程要比这里复杂一些,本文不会过多讨论。
若是还有新节点(好比s3节点)继续加入组,s3将从s1或s2中选一个,并与之创建异步复制的通道,而后获取缺失的数据,同步结束后,若是s1和s2都赞成s3加入,那么s3将会组中的新成员。其他节点加入组也依次类推。
有两点须要注意:
新节点加入组时,如何选择联系对象?
上面说加入第二个节点s2时会联系s1,加入s3时会联系s一、s2中的任意一个。实际上,新节点加入组时联系的对象,称为donor,意为数据供应者。新节点会和选中的donor创建异步复制通道,并从donor处获取缺失的数据。
在配置组复制时,须要指定种子节点列表。当新节点加入组时,只会联系种子节点,也便是说,只有种子节点列表中的节点才有机会成为donor,没有在种子节点列表中的节点不会被新节点选中。但建议,将组中全部节点都加入到种子列表中。
当联系第一个donor失败后,会向后联系第二个donor,再失败将联系第三个donor,若是全部种子节点都联系失败,在等待一段时间后再次从头开始联系第一个donor。依此类推,直到加组失败报错。
新节点加入组时,须要征得哪些节点的赞成?
实际上,新节点加组涉及到组的决策:是否容许它加组。在组复制中,全部的决策都须要组中大多数节点达成一致,也便是达到法定票数。所谓大多数节点,指的是N/2+1
(N是组中目前节点总数),例如目前组中有5个节点,则须要3个节点才能达到大多数的要求。
当主节点s1故障后,组复制的故障探测机制就能发现这个问题并报告给组中其余成员,组中各成员根据收集到的其余成员信息,会比较各成员的权重值(由变量group_replication_member_weigth
控制),权重值最高的优先成为新的Master。若是有多个节点具备相同的最高权重值,会按字典顺序比较它们的server_uuid值,最小的(升序排序,最小值在最前面)优先成为新的master。
但须要注意,变量group_replication_member_weigth
是从MySQL 5.7.20开始提供的,在MySQL 5.7.17到5.7.19之间没有该变量。此时将根据它们的server_uuid值进行排序选举。具体的规则可自行测试。
MySQL组复制使用Paxos分布式算法来提供节点间的分布式协调。正因如此,它要求组中大多数节点在线才能达到法定票数,从而对一个决策作出一致的决定。
大多数指的是N/2+1
(N是组中目前节点总数),例如目前组中有5个节点,则须要3个节点才能达到大多数的要求。因此,容许出现故障的节点数量以下图:
若想了解更多组复制的理论以及组复制中每个过程的细节,请参考我另外一篇文章MySQL组复制理论透彻分析,或者阅读我对MySQL官方手册关于组复制的翻译。
本文配置3个节点的单主模型组复制。配置很简单,基本上就是在常规复制选项的基础上多了几个选项、多了几步操做。
拓扑图以下:
具体环境细节以下:
节点名称 | MySQL版本 | 客户端接口(eth0) | 组内通讯接口(eth0) | 数据状态 |
---|---|---|---|---|
s1 | MySQL 5.7.22 | 192.168.100.21 | 192.168.100.21 | 全新实例 |
s2 | MySQL 5.7.22 | 192.168.100.22 | 192.168.100.22 | 全新实例 |
s3 | MySQL 5.7.22 | 192.168.100.23 | 192.168.100.23 | 全新实例 |
发现了每一个节点都给了两个接口吗?我这里配置它们都使用同一个接口eth0。其中:
s1<-->s二、s1<-->s三、s2<-->s3
。请确保这3个节点的主机名不一样,且能正确解析为客户端接口的地址(别搞错地址了),由于在链接donor进行数据恢复的时候,是经过主机名进行解析的。因此,全部节点都要先配置好不一样的主机名,并修改/etc/hosts文件。对于克隆出来的实验主机,这一步骤很关键。以centos 7为例:
# s1上: hostnamectl set-hostname --static s1.longshuai.com hostnamectl -H root@192.168.100.22 set-hostname s2.longshuai.com hostnamectl -H root@192.168.100.23 set-hostname s3.longshuai.com # 写/etc/hosts # s1上: cat >>/etc/hosts<<eof 192.168.100.21 s1.longshuai.com 192.168.100.22 s2.longshuai.com 192.168.100.23 s3.longshuai.com eof scp /etc/hosts 192.168.100.22:/etc scp /etc/hosts 192.168.100.23:/etc
1.先提供配置文件。
[mysqld] datadir=/data socket=/data/mysql.sock server-id=100 # 必须 gtid_mode=on # 必须 enforce_gtid_consistency=on # 必须 log-bin=/data/master-bin # 必须 binlog_format=row # 必须 binlog_checksum=none # 必须 master_info_repository=TABLE # 必须 relay_log_info_repository=TABLE # 必须 relay_log=/data/relay-log # 必须,若是不给,将采用默认值 log_slave_updates=ON # 必须 sync-binlog=1 # 建议 log-error=/data/error.log pid-file=/data/mysqld.pid transaction_write_set_extraction=XXHASH64 # 必须 loose-group_replication_group_name="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" # 必须 loose-group_replication_start_on_boot=off # 建议设置为OFF loose-group_replication_member_weigth = 40 # 非必需,mysql 5.7.20才开始支持该选项 loose-group_replication_local_address="192.168.100.21:20001" # 必须,下一行也必须 loose-group_replication_group_seeds="192.168.100.21:20001,192.168.100.22:20002"
想要使用组复制,要求仍是挺多的。分析一下上面的配置选项:
loose_
开头表示即便启动组复制插件,MySQL也继续正常容许下去。这个前缀是可选的。XXHASH64
的算法进行hash。所谓写集,是对事务中所修改的行进行的惟一标识,在后续检测并发事务之间是否修改同一行冲突时使用。它基于主键生成,因此使用组复制,表中必需要有主键。uuidgen
工具来生成UUID值。[root@xuexi ~]# uuidgen 09c38ef2-7d81-463e-bdb4-9459b2c0e49b
如今配置文件已经提供。能够启动mysql实例了。
[root@xuexi ~]# systemctl start mysqld
2.建立复制用户。
连上s1节点。建立用于复制的用户。我这里建立的用户为repl
,密码为P@ssword1!
。
mysql> create user repl@'192.168.100.%' identified by 'P@ssword1!'; mysql> grant replication slave on *.* to repl@'192.168.100.%';
3.配置节点加组时的通道。这是组复制的一个关键。
在新节点加入组时,首先要选择donor。新节点和donor之间的异步复制就是经过一个名为group_replication_recovery
的通道(通道名固定,不可以使用自定义通道)进行数据恢复的,通过数据恢复后,新节点填充了它缺失的那部分数据,这样就和组内其余节点的数据保持了同步。
这个恢复过程比较复杂,它是一种分布式恢复。本文不介绍这个,在MySQL组复制理论透彻分析中,我对此作了详细的说明。
执行change master to
语句设置恢复通道。
mysql> change master to master_user='repl', master_password='P@ssword1!' for channel 'group_replication_recovery';
这里的用户名、密码和通道在组复制中有一个专门的术语:通道凭据(channel credentials)。通道凭据是链接donor的关键。
当执行完上面的语句后,就生成了一个该通道的relay log文件(注意称呼:该通道的relay log,后面还有另外一个通道的relay log)。以下,其中前缀"relay-log"是配置文件中"relay_log"选项配置的值。
[root@xuexi ~]# ls -1 /data/*group* /data/relay-log-group_replication_recovery.000001 /data/relay-log-group_replication_recovery.index
group_replication_recovery
通道的relay log用于新节点加入组时,当新节点联系上donor后,会从donor处以异步复制的方式将其binlog复制到这个通道的relay log中,新节点将从这个recovery通道的relay log中恢复数据。
前面配置文件中已经指定了master和relay log的元数据信息要记录到表中,因此这里能够先查看下关于relay log的元数据。
mysql> select * from mysql.slave_relay_log_info\G *************************** 1. row *************************** Number_of_lines: 7 Relay_log_name: /data/relay-log-group_replication_recovery.000001 Relay_log_pos: 4 Master_log_name: Master_log_pos: 0 Sql_delay: 0 Number_of_workers: 0 Id: 1 Channel_name: group_replication_recovery
若是要查看链接master的元数据信息,则查询mysql.slave_master_info
表。不过如今不必查,由于啥都还没作呢。
4.安装组复制插件,并启动组复制功能。
一切就绪后,能够开启mysql实例的组复制功能了。
mysql> install plugin group_replication soname 'group_replication.so';
而后开启组复制功能。
mysql> set @@global.group_replication_bootstrap_group=on; mysql> start group_replication; mysql> set @@global.group_replication_bootstrap_group=off;
这里的过程很重要,须要引发注意。在开启组复制以前,设置全局变量group_replication_bootstrap_group
为on,这表示稍后启动的组复制功能将引导组,也就是建立组并配置组,这些都是自动的。配置引导变量为ON后,再开启组复制插件功能,也就是启动组复制。最后将引导变量设回OFF,之因此要设置回OFF,是为了不下次重启组复制插件功能时再次引导建立一个组,这样会存在两个名称相同实际却不相同的组。
这几个过程不适合放进配置文件中,强烈建议手动执行它们的。不然下次重启mysql实例时,会自动从新引导建立一个组。同理,除了第一个节点,其余节点启动组复制功能时,不该该引导组,因此只需执行其中的start语句,千万不能开启group_replication_bootstrap_group
变量引导组。
这里的几个过程,应该造成一个习惯,在启动第一个节点时,这3条语句同时执行,在启动其余节点时,只执行start语句。
当启动组复制功能后,将生成另外一个通道group_replication_applier
的相关文件。
[root@xuexi ~]# ls -1 /data/*group* /data/relay-log-group_replication_applier.000001 /data/relay-log-group_replication_applier.000002 /data/relay-log-group_replication_applier.index /data/relay-log-group_replication_recovery.000001 /data/relay-log-group_replication_recovery.index
是否还记得刚才用于恢复的通道group_replication_recovery
?这个applier通道是干什么的?常规复制有两个复制线程:io线程和sql线程,在组复制中,再也不称之为io_thread和sql_thread,取而代之的是receiver、certifier和applier。这里简单介绍一下它们的做用:
relay-log-group_replication_applier.00000N
。并非说组复制中没有io线程和sql线程,而是称呼改变了,receiver和applier实际上就是io_therad和sql_thread。
5.验证组中节点并测试插入不知足组复制要求的数据。
至此,这个节点的组复制已经配置完成了。如今须要查看这个节点是否成功加入到组中,成功加入组的标志是被设置为"ONLINE"。只要没有设置为ONLINE,就表示组中的这个节点是故障的。
查看的方式是经过查询performance_schema
架构下的replication_group_members
表。在这个架构下,有几张对于维护组复制来讲很是重要的表,这里的replication_group_members
是其中一张。关于其余的表,我会在有须要的地方或者其余文章中解释。
mysql> select * from performance_schema.replication_group_members; +---------------------------+--------------------------------------+---------------------+-------------+--------------+ | CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | +---------------------------+--------------------------------------+---------------------+-------------+--------------+ | group_replication_applier | a659234f-6aea-11e8-a361-000c29ed4cf4 | xuexi.longshuai.com | 3306 | ONLINE | +---------------------------+--------------------------------------+---------------------+-------------+--------------+
若是不方便观看,换一种显示方式:
mysql> select * from performance_schema.replication_group_members\G *************************** 1. row *************************** CHANNEL_NAME: group_replication_applier MEMBER_ID: a659234f-6aea-11e8-a361-000c29ed4cf4 MEMBER_HOST: xuexi.longshuai.com MEMBER_PORT: 3306 MEMBER_STATE: ONLINE
请注意这里的每一行,包括member_host,它是对外链接的地址,因此应该设置它的DNS解析为提供MySQL数据库服务的接口地址。这很重要,若是你不想去修改DNS解析,能够在启动组复制以前,设置report_host变量为对外的IP地址,或者将其写入到配置文件中。
如今,组中的这个节点已是ONLINE了,表示能够对外提供组复制服务了。
稍后,将向组中加入第二个节点s2和第三个节点s3,但在加入新节点以前,先向s1节点写入一些数据,顺便测试一下开启组复制后,必须使用InnoDB、表中必须有主键的限制。
下面建立4个表:t1和t4是InnoDB表,t3和t4具备主键。
create table t1(id int); create table t2(id int)engine=myisam; create table t3(id int primary key)engine=myisam; create table t4(id int primary key);
虽然说组复制对这些有限制,可是建立时是不会报错的。
向这4张表中插入数据:
insert into t1 values(1); insert into t2 values(1); insert into t3 values(1); insert into t4 values(1);
会发现只有t4能插入成功,t一、t二、t3都插入失败,报错信息以下:
ERROR 3098 (HY000): The table does not comply with the requirements by an external plugin.
意思是该表不听从外部插件(即组复制插件)的要求。
最后,查看下二进制日志中的事件。为了排版,我将显示结果中的日志名称列去掉了。
mysql> SHOW BINLOG EVENTS in 'master-bin.000004'; +------+----------------+-----------+-------------+-------------------------------------------------------------------+ | Pos | Event_type | Server_id | End_log_pos | Info | +------+----------------+-----------+-------------+-------------------------------------------------------------------+ | 4 | Format_desc | 100 | 123 | Server ver: 5.7.22-log, Binlog ver: 4 | | 123 | Previous_gtids | 100 | 150 | | | 150 | Gtid | 100 | 211 | SET @@SESSION.GTID_NEXT= 'a659234f-6aea-11e8-a361-000c29ed4cf4:1' | | 211 | Query | 100 | 399 | CREATE USER 'repl'@'192.168.100.%' IDENTIFIED WITH 'password' | | 399 | Gtid | 100 | 460 | SET @@SESSION.GTID_NEXT= 'a659234f-6aea-11e8-a361-000c29ed4cf4:2' | | 460 | Query | 100 | 599 | GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.100.%' | | 599 | Gtid | 100 | 660 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:1' | | 660 | Query | 100 | 719 | BEGIN | | 719 | View_change | 100 | 858 | view_id=15294216022242634:1 | | 858 | Query | 100 | 923 | COMMIT | | 923 | Gtid | 100 | 984 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:2' | | 984 | Query | 100 | 1083 | create database gr_test | | 1083 | Gtid | 100 | 1144 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:3' | | 1144 | Query | 100 | 1243 | use `gr_test`; create table t1(id int) | | 1243 | Gtid | 100 | 1304 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:4' | | 1304 | Query | 100 | 1416 | use `gr_test`; create table t2(id int)engine=myisam | | 1416 | Gtid | 100 | 1477 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:5' | | 1477 | Query | 100 | 1601 | use `gr_test`; create table t3(id int primary key)engine=myisam | | 1601 | Gtid | 100 | 1662 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:6' | | 1662 | Query | 100 | 1773 | use `gr_test`; create table t4(id int primary key) | | 1773 | Gtid | 100 | 1834 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:7' | | 1834 | Query | 100 | 1905 | BEGIN | | 1905 | Table_map | 100 | 1949 | table_id: 117 (gr_test.t4) | | 1949 | Write_rows | 100 | 1985 | table_id: 117 flags: STMT_END_F | | 1985 | Xid | 100 | 2012 | COMMIT /* xid=63 */ | +------+----------------+-----------+-------------+-------------------------------------------------------------------+
除了正常的事务对应的事件,须要关注的三行是:
BEGIN View_change <----> view_id=15294216022242634:1 COMMIT
组复制中,每一个决策都须要组中大多数节点达成一致,包括新节点加组、离组的决定。实际上,组复制中内置了一个组成员服务,这个服务负责组成员的配置以及动态捕获组中成员列表,这个成员列表成为成员视图。每一个视图都有一个view id,view id的第一部分是建立组时随机生成的,只要组不中止,这部分就不改变,第二部分是从1开始的单调递增的数值。每当有成员加组、离组时,都会触发这个服务对组成员进行从新配置,每次组成员的从新配置,view id的第二部分都会单调递增地加1,表示这是新的成员视图,新的组成员视图须要获得组中大多数节点的赞成,因此这个消息要在组中进行传播。如何传播?就是经过将视图更改的事件做为一个事务写进binlog中,而后在组中处处复制,这样每一个节点均可收到视图变化的消息,并对此作出回应,赞成以后再commit这个事务。若是足够细心,会发现这个事务的提交和下面插入数据的提交(COMMIT /* xid=63 */
)方式不同。若是不理解也不要紧,这个理论并不影响组复制的使用。
再仔细一看,还能够发现MySQL中的DDL语句是没有事务的。因此,毫不容许不一样节点上对同一个对象并发执行"DDL+DML"和"DDL+DDL",冲突检测机制会探测到这样的冲突。
当组中已有第一个节点后,须要作的是向组中添加新的节点。这里以添加s2和s3为例。
前面屡次提到,新节点在加入组的时候,会先选择一个donor,并经过异步复制的方式从这个donor处获取缺失的数据,以便在成功加入组的时候它的数据和组中已有的节点是彻底同步的,这样才能向外界客户端提供查询。
这里的重点在于异步复制,既然是复制,它就须要复制binlog,并经过应用binlog中的记录来写数据。若是在加入组以前,组中的数据量已经很是大,那么这个异步复制的过程会很慢,并且还会影响donor的性能,毕竟它要传输大量数据出去。
原本加入新节点的目的就是对组复制进行扩展,提升它的均衡能力,如今由于异步复制慢,反而致使性能稍有降低,新节点短时间内还没法上线向外提供服务。这有点背离本来的目标。
再者,若是组中的节点purge过日志,那么新节点将没法从donor上获取完整的数据。这时新节点上的恢复过程会让它从新选择下一个donor。但极可能仍是会失败,由于实际环境中,既然purge了某节点上的一段日志,极可能同时会去全部节点上也Purge。(注意,purge不是事件,不会写入到binlog中,因此不会复制到其它节点上,换句话说,某节点Purge后,那么它的binlog和其它节点的binlog是不一致的)。
因此,在新节点加入组以前,应该先经过备份恢复的方式,从组中某节点上备份目前的数据到新节点上,而后再让新节点去加组,这样加组的过程将很是快,且能保证不会由于purge的缘由而加组失败。至于如何备份恢复,参见个人另外一篇文章:将slave恢复到master指定的坐标。
我这里作实验的环境,全部节点都是刚安装好的全新实例,数据量小,也没purge过日志,因此直接加入到组中就能够。
仍然先是提供配置文件。配置文件和第一个节点基本相同,除了几个须要保持惟一性的选项。
配置文件内容以下:
[mysqld] datadir=/data socket=/data/mysql.sock server-id=110 # 必须,每一个节点都不能相同 gtid_mode=on # 必须 enforce_gtid_consistency=on # 必须 log-bin=/data/master-bin # 必须 binlog_format=row # 必须 binlog_checksum=none # 必须 master_info_repository=TABLE # 必须 relay_log_info_repository=TABLE # 必须 relay_log=/data/relay-log # 必须,若是不给,将采用默认值 log_slave_updates=ON # 必须 sync-binlog=1 # 建议 log-error=/data/error.log pid-file=/data/mysqld.pid transaction_write_set_extraction=XXHASH64 # 必须 loose-group_replication_group_name="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" # 必须 loose-group_replication_start_on_boot=off # 建议设置为OFF loose-group_replication_member_weigth = 20 # 非必需,mysql 5.7.20才开始支持该选项 loose-group_replication_local_address="192.168.100.22:20002" # 必须,下一行也必须 loose-group_replication_group_seeds="192.168.100.21:20001,192.168.100.22:20002"
这里和s1的配置文件相比,只修改了server-id
和group_replication_local_address
以及权重值。
而后执行change master to
,选择一个donor(此刻只有s1能选),并和donor创建通道链接。
mysql> change master to master_user='repl', master_password='P@ssword1!' for channel 'group_replication_recovery';
这时,就已经选择好donor,并和donor创建通道链接了。若是去s1上查看,能够看到这个通道的链接。下面的查询结果中,第二行就是和s2创建的链接,通道为group_replication_recovery
。
mysql> select * from mysql.slave_master_info\G *************************** 1. row *************************** Number_of_lines: 25 Master_log_name: Master_log_pos: 4 Host: <NULL> User_name: User_password: Port: 0 Connect_retry: 60 Enabled_ssl: 0 Ssl_ca: Ssl_capath: Ssl_cert: Ssl_cipher: Ssl_key: Ssl_verify_server_cert: 0 Heartbeat: 30 Bind: Ignored_server_ids: 0 Uuid: Retry_count: 86400 Ssl_crl: Ssl_crlpath: Enabled_auto_position: 1 Channel_name: group_replication_applier Tls_version: *************************** 2. row *************************** Number_of_lines: 25 Master_log_name: Master_log_pos: 4 Host: User_name: repl User_password: P@ssword1! Port: 3306 Connect_retry: 60 Enabled_ssl: 0 Ssl_ca: Ssl_capath: Ssl_cert: Ssl_cipher: Ssl_key: Ssl_verify_server_cert: 0 Heartbeat: 0 Bind: Ignored_server_ids: 0 Uuid: Retry_count: 86400 Ssl_crl: Ssl_crlpath: Enabled_auto_position: 0 Channel_name: group_replication_recovery Tls_version: 2 rows in set (0.00 sec)
而后回到s2节点上,安装组复制插件,并开启组复制功能。
mysql> install plugin group_replication soname 'group_replication.so'; mysql> start group_replication;
组复制启动成功后,查看是否处于online状态。(请无视我这里的Member_host字段,这是我设置了report_host变量的结果)
mysql> select * from performance_schema.replication_group_members\G *************************** 1. row *************************** CHANNEL_NAME: group_replication_applier MEMBER_ID: a5165443-6aec-11e8-a8f6-000c29827955 MEMBER_HOST: 192.168.100.22 MEMBER_PORT: 3306 MEMBER_STATE: ONLINE *************************** 2. row *************************** CHANNEL_NAME: group_replication_applier MEMBER_ID: a659234f-6aea-11e8-a361-000c29ed4cf4 MEMBER_HOST: xuexi.longshuai.com MEMBER_PORT: 3306 MEMBER_STATE: ONLINE
再查看数据是否已经同步到s2节点。其实显示了ONLINE,就必定已经同步。
mysql> show tables from gr_test; +-------------------+ | Tables_in_gr_test | +-------------------+ | t1 | | t2 | | t3 | | t4 | +-------------------+ 4 rows in set (0.00 sec) mysql> select * from gr_test.t4; +----+ | id | +----+ | 1 | +----+
查看binlog事件。会发现内容已经复制,且view id又发生了一次变化。
mysql> show binlog events in 'master-bin.000002'; +------+----------------+-----------+-------------+-------------------------------------------------------------------+ | Pos | Event_type | Server_id | End_log_pos | Info | +------+----------------+-----------+-------------+-------------------------------------------------------------------+ | 4 | Format_desc | 110 | 123 | Server ver: 5.7.22-log, Binlog ver: 4 | | 123 | Previous_gtids | 110 | 150 | | | 150 | Gtid | 100 | 211 | SET @@SESSION.GTID_NEXT= 'a659234f-6aea-11e8-a361-000c29ed4cf4:1' | | 211 | Query | 100 | 399 | CREATE USER 'repl'@'192.168.100.%' IDENTIFIED WITH 'password' | | 399 | Gtid | 100 | 460 | SET @@SESSION.GTID_NEXT= 'a659234f-6aea-11e8-a361-000c29ed4cf4:2' | | 460 | Query | 100 | 599 | GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.100.%' | | 599 | Gtid | 100 | 660 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:1' | | 660 | Query | 100 | 719 | BEGIN | | 719 | View_change | 100 | 858 | view_id=15294216022242634:1 | | 858 | Query | 100 | 923 | COMMIT | | 923 | Gtid | 100 | 984 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:2' | | 984 | Query | 100 | 1083 | create database gr_test | | 1083 | Gtid | 100 | 1144 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:3' | | 1144 | Query | 100 | 1243 | use `gr_test`; create table t1(id int) | | 1243 | Gtid | 100 | 1304 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:4' | | 1304 | Query | 100 | 1416 | use `gr_test`; create table t2(id int)engine=myisam | | 1416 | Gtid | 100 | 1477 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:5' | | 1477 | Query | 100 | 1601 | use `gr_test`; create table t3(id int primary key)engine=myisam | | 1601 | Gtid | 100 | 1662 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:6' | | 1662 | Query | 100 | 1773 | use `gr_test`; create table t4(id int primary key) | | 1773 | Gtid | 100 | 1834 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:7' | | 1834 | Query | 100 | 1893 | BEGIN | | 1893 | Table_map | 100 | 1937 | table_id: 112 (gr_test.t4) | | 1937 | Write_rows | 100 | 1973 | table_id: 112 flags: STMT_END_F | | 1973 | Xid | 100 | 2000 | COMMIT /* xid=31 */ | | 2000 | Gtid | 100 | 2061 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:8' | | 2061 | Query | 100 | 2120 | BEGIN | | 2120 | View_change | 100 | 2299 | view_id=15294216022242634:2 | | 2299 | Query | 100 | 2364 | COMMIT | +------+----------------+-----------+-------------+-------------------------------------------------------------------+
和加入s2节点几乎一致。因此这里作个步骤的简单总结:
1.配置主机名和DNS解析。
见2.配置单主模型。
2.提供配置文件,并启动MySQL实例。
datadir=/data socket=/data/mysql.sock server-id=120 gtid_mode=on enforce_gtid_consistency=on log-bin=/data/master-bin binlog_format=row binlog_checksum=none master_info_repository=TABLE relay_log_info_repository=TABLE relay_log=/data/relay-log log_slave_updates=ON sync-binlog=1 log-error=/data/error.log pid-file=/data/mysqld.pid 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_member_weigth = 30 loose-group_replication_local_address="192.168.100.23:20003" loose-group_replication_group_seeds="192.168.100.21:20001,192.168.100.22:20002"
3.连上新实例,设置恢复通道的凭据。
change master to master_user='repl', master_password='P@ssword1!' for channel 'group_replication_recovery';
4.安装组复制插件,并启动组复制功能。
install plugin group_replication soname 'group_replication.so'; start group_replication;
5.查看新节点是否已经处于ONLINE。
select * from performance_schema.replication_group_members\G
当加入一个新节点时,一切配置都正确,可是新节点死活就是不一样步数据,随便执行一个语句都卡半天,查看performance_schema.replication_group_members
表时,还发现这个新节点一直处于recovering装态。
这时,请查看新节点的错误日志。如下是我截取出来的一行。
[root@xuexi ~]# tail /data/error.log 2018-06-19T17:41:22.314085Z 10 [ERROR] Plugin group_replication reported: 'There was an error when connecting to the donor server. Please check that group_replication_recovery channel credentials and all MEMBER_HOST column values of performance_schema.replication_group_members table are correct and DNS resolvable.'
很显然,链接donor的时候出错,让咱们检测通道凭据,而且查看member_host字段的主机名是否正确解析。一切正确配置的状况下,通道凭据是没错的,错就错在member_host的主机名。
当和donor创建通道链接时,首先会经过member_host字段的主机名去解析donor的地址。这个主机名默认采起的是操做系统默认的主机名,而非ip地址。因此,必须设置DNS解析,或者/etc/hosts文件,将member_host对应的主机名解析为donor的ip地址。
我这里之因此显示错误,是由于我在测试环境下,全部节点的主机名都相同:xuexi.longshuai.com。因此新节点会将这个主机名解析到本机。
若是新节点中包含了额外的数据,例如,新节点上多了一个用户,建立这个用户是会产生gtid事务的,当这个节点要加入到组时会报错。如下是error.log中的内容:
2018-06-24T12:56:29.300453Z 0 [ERROR] Plugin group_replication reported: 'This member has more executed transactions than those present in the gro up. Local transactions: 48f1d8aa-7798-11e8-bf9a-000c29296408:1-2 > Group transactions: 481024ff-7798-11e8-89da-000c29ff1054:1-4, bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb:1-6' 2018-06-24T12:56:29.300536Z 0 [ERROR] Plugin group_replication reported: 'The member contains transactions not present in the group. The member wi ll now exit the group.'
错误已经指明了是新节点的事务和组中的事务不匹配,有多余的事务,从而致使加组失败。
若是你已经明确这些多余的事务形成的数据不会影响到组中的节点,正如多了一个可能永远也用不上的用户,或者多了几个和组复制彻底无关的数据库。
这时,能够将这些可有可无的gtid给删掉,可是想删除这些gtid还真没那么容易。purge日志不行,停掉MySQL后删日志文件也不行,把binlog关掉再打开也不行。它们都会把之前的事务记录到Previous_gtid中。真正行之有效的方法是将全局变量executed_gtid设置为空。方法为:
mysql> reset master;
而后,再去加组。
操做组复制的语句只有两个。
start group_replication; stop group_replication;
可是,和组复制相关的变量却有好几个。
当要中止组中的某个成员中的组复制功能时,须要在那个节点上执行stop group_replication
语句。但必定要注意,在执行这个语句以前,必需要保证这个节点不会向外提供MySQL服务,不然有可能会有新数据写入(例如主节点中止时),或者读取到过时数据。
因此,要安全地重启整个组,最佳方法是先中止全部非主节点的MySQL实例(不只是中止组复制功能),而后中止主节点的MySQL实例,再先重启主节点,在这个节点上引导组,并启动它的组复制功能。最后再将各slave节点加入组。
若是只是想中止某单个节点,若是这个节点是主节点,那么中止整个MySQL实例,若是是slave节点,那么只需中止它的组复制功能便可。当它们须要再次加组时,只需执行start group_replication
语句。
那么,如何知道哪一个节点是主节点?
只有单主模型的组复制才须要查找主节点,多主模型没有master/slave的概念,因此无需查找。
mysql> SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='group_replication_primary_member'; +--------------------------------------+ | VARIABLE_VALUE | +--------------------------------------+ | a659234f-6aea-11e8-a361-000c29ed4cf4 | +--------------------------------------+ 1 row in set (0,00 sec)
或者:
mysql> SHOW STATUS LIKE 'group_replication_primary_member';
这样查找只是获取了主节点的uuid,能够表链接的方式获取主节点主机名。
select b.member_host the_master,a.variable_value master_uuid from performance_schema.global_status a join performance_schema.replication_group_members b on a.variable_value = b.member_id where variable_name='group_replication_primary_member'; +------------------+--------------------------------------+ | the_master | master_uuid | +------------------+--------------------------------------+ | s1.longshuai.com | a659234f-6aea-11e8-a361-000c29ed4cf4 | +------------------+--------------------------------------+
组复制中,有两种节点离组的状况:自愿离组、非自愿离组。
stop group_replication;
语句。
目前,组中有3个节点:s一、s2和s3,其中s1是主节点。
如今将主节点直接关机或者断掉网卡,模拟非自愿离组。
# s1上: shell> ifconfig eth0 down
而后查看s2上的错误日志。能够看到选举新主节点的过程。
[Warning] group_replication reported: 'Member with address s1.longshuai.com:3306 has become unreachable.' [Note] group_replication reported: '[GCS] Removing members that have failed while processing new view.' [Warning] group_replication reported: 'Members removed from the group: s1.longshuai.com:3306' [Note] group_replication reported: 'Primary server with address s1.longshuai.com:3306 left the group. Electing new Primary.' [Note] group_replication reported: 'A new primary with address s2.longshuai.com:3306 was elected, enabling conflict detection until the new primary applies all relay logs.' [Note] group_replication reported: 'This server is working as primary member.' [Note] group_replication reported: 'Group membership changed to s2.longshuai.com:3306, s3.longshuai.com:3306 on view 15294358712349771:4.'
这里将s2选为新的主节点,且告知成员视图中目前组中成员变为s2和s3。
能够测试下,是否能向新的主节点s2中插入数据。
# s2上: mysql> insert into gr_test.t4 values(333);
若是再将s3停掉呢?还能继续写入数据吗?
# 在s3上: shell> ifconfig eth0 down
回到s2,插入数据看看:
# s2上: mysql> insert into gr_test.t4 values(3333);
发现没法插入,一直阻塞。
查看下s2的错误日志:
[Warning] group_replication reported: 'Member with address s3.longshuai.com:3306 has become unreachable.' [ERROR] group_replication reported: 'This server is not able to reach a majority of members in the group. This server will now block all updates. The server will remain blocked until contact with the majority is restored. It is possible to use group_replication_force_members to force a new group membership.'
已经说明了,s3移除后,组中的成员没法达到大多数的要求,因此将复制组给阻塞了。若是想要修复组,能够强制生成一个新的组成员视图。
若是这时候,将s1和s3的网卡启动,s1和s3还会加入到组中吗?如下是s2上的错误日志:
[Warning] group_replication reported: 'Member with address s3.longshuai.com:3306 is reachable again.' [Warning] group_replication reported: 'The member has resumed contact with a majority of the members in the group. Regular operation is restored and transactions are unblocked.'
发现s3加入了,但s1未加入。为何?由于s1节点上只是停掉了网卡,mysql实例以及组复制功能还在运行,并且它的角色还保持为主节点。这时候,s1和s二、s3已经出现了所谓的"网络分裂",对于s2和s3来讲,s1被隔离,对于s1来讲,s2和s3被隔离。当s1的网卡恢复后,它仍然保留着本身的主节点运行,但由于它达不到大多数的要求,因此s1是被阻塞的,若是网卡长时间没有恢复,则s1会被标记为ERROR。
这种状况下的s1,要让它从新加入到组中,应该重启组复制,更安全的方法是重启mysql实例,由于组可能尚未标记为ERROR,这个组暂时还存在,它与s二、s3所属的组同名,可能会致使脑裂问题。
若是是自愿离组呢?能够测试下,不管自愿退出多少个节点,只要组中还有节点,组都不会被阻塞。