绑定完请刷新页面
取消
刷新

分享好友

×
取消 复制
死磕数据库系列(二十六):MySQL 高可用之单主、双主模型组复制配置实践
2023-03-16 14:26:41


前面我们学习:MySQL 高可用之组复制(MGR)技术的相关原理知识,今天我将详细的为大家介绍 MySQL 高可用技术组复制的单主、双主两种模型下的原理及配置相关知识,希望大家能够从中收获多多!如有帮助,请点在看转发支持一波!!!

MySQL的组复制可以配置为单主模型和多主模型两种工作模式,它们都能保证MySQL的高可用。

而使用单主模型的组复制就简单的太多了,需要知道的就是它会自动选举master节点这个特性,因为它的维护一切都是自动进行的,甚至对于管理人员来说,完全可以不用去了解组复制的理论。

虽然单主模型比多主模型的性能要差,但它没有数据不一致的危险,加上限制少,配置简单,基本上没有额外的学习成本,所以多数情况下都是配置单主模型的组复制,即使是PXC和MariaDB也如此。更多关于MySQL学习的文章,请参阅:死磕数据库系列之 MySQL ,本系列持续更新中。

MySQL 高可用之配置单主模型的组复制

单主模型组复制的理论基础

虽说组复制的单主模型很简单,但有必要了解一点和单主模型有关的理论,尽管不了解也没什么问题,毕竟一切都是自动的。

如下图,master节点为s1,其余为slave节点。组复制一切正常时,所有的写操作都路由到s1节点上,所有的读操作都路由到s2、s3、s4或s5上。当s1节点故障后,组复制自动选举新的master节点。假如选举s2为新master成功后,s3、s4和s5将指向s2,写操作将路由到s2节点上。

至于如何改变客户端的路由目标,这不是组复制应该考虑的事情,而是客户端应用程序应该考虑的事情。实际上,更好的方式是使用中间件来做数据库的路由,比如 MySQL Router、ProxySQL、amoeba、cobar、mycat

如何加入新节点

上面一直说,单主模型是自动选举主节点的,那么如何选举?

首先,在个MySQL节点s1启动时,一般会将其设置为组的引导节点,所谓引导就是在启动组复制功能时去创建一个复制组。当然,这并非强制要求,也可以设置第二个启动节点作为组的引导节点。因为组内没有其他节点,所以这个节点会直接选为master节点。

然后,如果有第二个节点要加入组时,新节点需要征得组的同意,因为目前只有一个节点,所以只需s1节点同意即可。新节点在加入组时,首先会联系s1,与s1建立异步复制的通道,并从s1节点处获取s2上目前缺失的数据,等到s1和s2节点上的数据同步后,s2节点就会真正成为组中的新成员。当然,实际过程要比这里复杂一些,本文不会过多讨论。

如果还有新节点(比如s3节点)继续加入组,s3将从s1或s2中选一个,并与之建立异步复制的通道,然后获取缺失的数据,同步结束后,如果s1和s2都同意s3加入,那么s3将会组中的新成员。其余节点加入组也依次类推。

有两点需要注意:

新节点加入组时,如何选择联系对象?

上面说加入第二个节点s2时会联系s1,加入s3时会联系s1、s2中的任意一个。实际上,新节点加入组时联系的对象,称为donor,意为数据供应者。新节点会和选中的donor建立异步复制通道,并从donor处获取缺失的数据。

在配置组复制时,需要指定种子节点列表。当新节点加入组时,只会联系种子节点,也即是说,只有种子节点列表中的节点才有机会成为donor,没有在种子节点列表中的节点不会被新节点选中。但建议,将组中所有节点都加入到种子列表中

当联系个donor失败后,会向后联系第二个donor,再失败将联系第三个donor,如果所有种子节点都联系失败,在等待一段时间后再次从头开始联系个donor。依此类推,直到加组失败报错。

新节点加入组时,需要征得哪些节点的同意?

实际上,新节点加组涉及到组的决策:是否允许它加组。在组复制中,所有的决策都需要组中大多数节点达成一致,也即是达到法定票数。所谓大多数节点,指的是N/2+1(N是组中目前节点总数),例如目前组中有5个节点,则需要3个节点才能达到大多数的要求。

如何选举新的master

当主节点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个节点才能达到大多数的要求。所以,允许出现故障的节点数量如下图:

配置单主模型

本文配置3个节点的单主模型组复制。配置很简单,基本上就是在常规复制选项的基础上多了几个选项、多了几步操作。

拓扑图如下:具体环境细节如下:

发现了每个节点都给了两个接口吗?我这里配置它们都使用同一个接口eth0。其中:

  • 1.客户端接口是mysqld向外提供数据库服务的,对应端口是3306,例如php程序连接MySQL执行一个查询语句时就使用该地址。
  • 2.组内节点通信接口用于组内各节点消息传递,组内两两节点建立一条消息传递的TCP连接。所以,3个节点需要建立的组内通信连接为:s1<-->s2、s1<-->s3、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

配置组内个节点s1

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"

想要使用组复制,要求还是挺多的。分析一下上面的配置选项:

  • (1).因为组复制基于GTID,所以必须开启gtid_mode和enforce_gtid_consistency。
  • (2).组复制必须开启二进制日志,且必须设置为行格式的二进制日志,这样才能从日志记录中收集信息且保证数据一致性。所以设置log_bin和binlog_format。
  • (3).由于MySQL对复制事件校验的设计缺陷,组复制不能对他们校验,所以设置binlog_checksum=none。
  • (4).组复制要将master和relay log的元数据写入到mysql.slave_master_info和mysql.slave_relay_log_info中。
  • (5).组中的每个节点都保留了完整的数据副本,它是share-nothing的模式。所以所有节点上都必须开启log_slave_updates,这样新节点随便选哪个作为donor都可以进行异步复制。
  • (6).sync_binlog是为了保证每次事务提交都立刻将binlog刷盘,保证出现故障也不丢失日志。
  • (7).后的6行是组复制插件的配置。以loose_开头表示即使启动组复制插件,MySQL也继续正常允许下去。这个前缀是可选的。
  • (8).倒数第6行表示写集合以XXHASH64的算法进行hash。所谓写集,是对事务中所修改的行进行的标识,在后续检测并发事务之间是否修改同一行冲突时使用。它基于主键生成,所以使用组复制,表中必须要有主键。
  • (9).倒数第5行表示这个复制组的名称。它必须是一个有效的UUID值。嫌可以直接和上面一样全写字母a。在Linux下,可以使用uuidgen工具来生成UUID值。
[root@xuexi ~]# uuidgen
09c38ef2-7d81-463e-bdb4-9459b2c0e49b
  • (10).倒数第4行表示组复制功能不随MySQL实例启动而启动。虽然,可以将组复制插件和启动组复制功能的选项写在配置文件里,但强烈建议不要如此,而是每次手动去配置。
  • (11).倒数第3行表示该节点在组中的权重为40。权重越高,自动选举为primary节点的优先级就越高。
  • (12).倒数第2行表示本机上用于组内各节点之间通信的地址和端口
  • (13).后一行,设置本组的种子节点。种子节点的意义在前文已经解释过了。

现在配置文件已经提供。可以启动mysql实例了。

[root@xuexi ~]# systemctl start mysqld

2.创建复制用户。

连上s1节点。创建用于复制的用户。我这里创建的用户为repl,密码为P@ssword1!

mysqlcreate user repl@'192.168.100.%' identified by 'P@ssword1!';
mysqlgrant replication slave on *.* to repl@'192.168.100.%';

3.配置节点加组时的通道。这是组复制的一个关键。

在新节点加入组时,首先要选择donor。新节点和donor之间的异步复制就是通过一个名为group_replication_recovery的通道(通道名固定,不可使用自定义通道)进行数据恢复的,经过数据恢复后,新节点填充了它缺失的那部分数据,这样就和组内其他节点的数据保持了同步。

这个恢复过程比较复杂,它是一种分布式恢复。

执行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: 
        Sql_delay: 
Number_of_workers: 
               Id1
     Channel_name: group_replication_recovery

如果要查看连接master的元数据信息,则查询mysql.slave_master_info表。不过现在没必要查,因为啥都还没做呢。

4.安装组复制插件,并启动组复制功能。

一切就绪后,可以开启mysql实例的组复制功能了。

mysql> install plugin group_replication soname 'group_replication.so';

然后开启组复制功能。

mysqlset @@global.group_replication_bootstrap_group=on;
mysqlstart group_replication;
mysqlset @@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。这里简单介绍一下它们的作用:

  • receiver的作用类似于io线程,用于接收组内个节点之间传播的消息和事务。也用于接收外界新发起的事务。
  • applier的作用类似于sql线程,用于应用relay log中的记录。不过,组复制的relay log不再是relay log,而是这里的组复制relay log:relay-log-group_replication_applier.00000N
  • certifier的作用在receiver接收到消息后,验证是否有并发事务存在冲突问题。冲突检测通过后,这条消息就会写入到组复制的relay log中,等待applier去应用。

并不是说组复制中没有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能插入成功,t1、t2、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",冲突检测机制会探测到这样的冲突。更多关于MySQL学习的文章,请参阅:死磕数据库系列之 MySQL ,本系列持续更新中。

向组中添加新节点

当组中已有个节点后,需要做的是向组中添加新的节点。这里以添加s2和s3为例。

添加新节点前要做什么

前面多次提到,新节点在加入组的时候,会先选择一个donor,并通过异步复制的方式从这个donor处获取缺失的数据,以便在成功加入组的时候它的数据和组中已有的节点是完全同步的,这样才能向外界客户端提供查询。

这里的重点在于异步复制,既然是复制,它就需要复制binlog,并通过应用binlog中的记录来写数据。如果在加入组之前,组中的数据量已经非常大,那么这个异步复制的过程会很慢,而且还会影响donor的性能,毕竟它要传输大量数据出去。

本来加入新节点的目的就是对组复制进行扩展,提高它的均衡能力,现在因为异步复制慢,反而导致性能稍有下降,新节点短期内还无法上线向外提供服务。这有点背离原本的目标。

再者,如果组中的节点purge过日志,那么新节点将无法从donor上获取完整的数据。这时新节点上的恢复过程会让它重新选择下一个donor。但很可能还是会失败,因为实际环境中,既然purge了某节点上的一段日志,很可能同时会去所有节点上也Purge。(注意,purge不是事件,不会写入到binlog中,所以不会复制到其它节点上,换句话说,某节点Purge后,那么它的binlog和其它节点的binlog是不一致的)。

所以,在新节点加入组之前,应该先通过备份恢复的方式,从组中某节点上备份目前的数据到新节点上,然后再让新节点去加组,这样加组的过程将非常快,且能保证不会因为purge的原因而加组失败。

我这里做实验的环境,所有节点都是刚安装好的全新实例,数据量小,也没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-idgroup_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: 
         Connect_retry: 60
           Enabled_ssl: 
                Ssl_ca: 
            Ssl_capath: 
              Ssl_cert: 
            Ssl_cipher: 
               Ssl_key: 
Ssl_verify_server_cert: 
             Heartbeat: 30
                  Bind: 
    Ignored_server_ids: 
                  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: 
                Ssl_ca: 
            Ssl_capath: 
              Ssl_cert: 
            Ssl_cipher: 
               Ssl_key: 
Ssl_verify_server_cert: 
             Heartbeat: 
                  Bind: 
    Ignored_server_ids: 
                  Uuid
           Retry_count: 86400
               Ssl_crl: 
           Ssl_crlpath: 
 Enabled_auto_position: 
          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 (.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.提供配置文件,并启动 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

加组失败

新节点一直处于recoveing?

当加入一个新节点时,一切配置都正确,但是新节点死活就是不同步数据,随便执行一个语句都卡半天,查看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事务?

如果新节点中包含了额外的数据,例如,新节点上多了一个用户,创建这个用户是会产生gtid事务的,当这个节点要加入到组时会报错。以下是error.log中的内容:

2018-06-24T12:56:29.300453 [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.300536 [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;

然后,再去加组。更多关于MySQL学习的文章,请参阅:死磕数据库系列之 MySQL ,本系列持续更新中。

组复制维护:停止、重启组复制功能

操作组复制的语句只有两个。

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 (,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;语句。

  • (1).执行该语句表示该节点自愿离组,它会触发视图自动配置,并将该视图更改操作复制到组内所有节点,直到大多数节点都同意新的视图配置,该节点才会离组。
  • (2).节点自愿离组时,不会丢失法定票数。所以无论多少个节点自愿离组,都不会出现"达不到大多数"的要求而阻塞组。
  • (3).举个例子,5个节点的组,自愿退出一个节点A后,这个组的大小为4。这个组认为节点A从来都没有出现过。

非自愿离组:除了上面自愿离组的情况,所有离组的情况都是非自愿离组。比如节点宕机,断网等等。

  • (1).节点非自愿离组时,故障探测机制会检测到这个问题,于是向组中报告这个问题。然后会触发组视图成员自动配置,需要大多数节点同意新视图。
  • (2).非自愿离组时,组的大小不会改变,无论多少个节点的组,节点非自愿退出后,组大小还是5,只不过这些离组的节点被标记为非ONLINE。但注意,组的视图配置会改变,因为离组的节点状态需要标记为非ONLINE。
  • (3).非自愿离组时,会丢失法定票数。所以,当非自愿离组节点数量过多时,导致组中剩余节点数量达不到大多数的要求,组就会被阻塞。
  • (4).举个例子,5节点的组,非自愿退出1个节点A后,这个组的大小还是5,但是节点A在新的视图中被标记为unreachable或其他状态。当继续非自愿退出2个节点后,组中只剩下2个ONLINE节点,这时达不到大多数的要求,组就会被阻塞。

目前,组中有3个节点:s1、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: '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和s2、s3已经出现了所谓的"网络分裂",对于s2和s3来说,s1被隔离,对于s1来说,s2和s3被隔离。当s1的网卡恢复后,它仍然保留着自己的主节点运行,但因为它达不到大多数的要求,所以s1是被阻塞的,如果网卡长时间没有恢复,则s1会被标记为ERROR。

这种情况下的s1,要让它重新加入到组中,应该重启组复制,更安全的方法是重启mysql实例,因为组可能还没有标记为ERROR,这个组暂时还存在,它与s2、s3所属的组同名,可能会导致脑裂问题。

如果是自愿离组呢?可以测试下,无论自愿退出多少个节点,只要组中还有节点,组都不会被阻塞。更多关于MySQL学习的文章,请参阅:死磕数据库系列之 MySQL ,本系列持续更新中。

MySQL 高可用之配置多主模型的组复制

组复制:单主和多主模型

MySQL组复制支持单主模型和多主模型,它们都能保证MySQL数据库的高可用。

单主模型下:

  • 只有一个主节点,该主节点负责所有的写操作,其他节点作为slave节点提供读取服务(会自动设置为read-only)。
  • 在主节点故障,单主模型会自动选举新的主节点。选举后,剩余节点将指向该节点。但是,客户端还是会有部分请求路由到故障的主节点上,因此需要想办法解决这样的问题。这不是MySQL该考虑解决的问题,而是客户端应用程序、数据库中间件(常见的:ProxySQL、MySQL Router、mycat、amoeba、cobar等)该解决的问题。
  • 只要非自愿离组的故障节点(自愿、非自愿离组)不超过大多数,组复制就不会被阻塞。

多主模型下:

  • 没有master和slave的概念。所有的节点都可以读、写数据。
  • 因为所有节点都能提供读写服务,所以性能较之单主模型要好一些。
  • 配置多主模型的工作方式,比单主模型的限制更多。
  • 节点非自愿故障后,除了影响一点性能,不会对组复制造成影响。除非故障的节点数过多,使得剩余在线节点达不到"大多数"的要求。

单主和多主模型配置文件的区别

以下是单主模型组复制的配置文件:

[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="ABCDEFGHIJK"   # 必须,下一行也必须
loose-group_replication_group_seeds="abcdefg"

多主模型和单主模型的配置文件基本相同,除了需要加入:

group_replication_enforce_update_everywhere_checks=ON  # 非必需,但强烈建议
group_replication_single_primary_mode=OFF  # 必须,表示关闭单主模型,即使用多主

需要注释权重行,因为多主模型下没有master的概念,所以无需选举的权重值。

# loose-group_replication_member_weigth = 40

此外,除非业务依赖于默认的repeatable read,否则建议将事务隔离级别设置为read committed,且不能设置为serializable级别(强制要求)。所以,如果允许,还可以加上:

transaction_isolation = 'read-committed' 

配置多主模型

本文打算配置5个节点的多主模型复制组。

具体环境细节如下:每个节点向外提供MySQL服务和组内通信都使用同一个接口。

1.修改主机名,添加DNS解析。

因为组内每个节点都使用主机名进行解析其他成员的地址,所以必须配置好主机名,并保证每个节点都能正确解析主机名。

在s1上执行:

hostnamectl set-hostname s1.longshuai.com
hostnamectl -H 192.168.100.22 set-hostname s2.longshuai.com
hostnamectl -H 192.168.100.23 set-hostname s3.longshuai.com
hostnamectl -H 192.168.100.24 set-hostname s4.longshuai.com
hostnamectl -H 192.168.100.25 set-hostname s5.longshuai.com

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
    192.168.100.24 s4.longshuai.com
    192.168.100.25 s5.longshuai.com
eof

scp /etc/hosts 192.168.100.22:/etc
scp /etc/hosts 192.168.100.23:/etc
scp /etc/hosts 192.168.100.24:/etc
scp /etc/hosts 192.168.100.25:/etc

2.提供配置文件。

以下是s1节点配置文件。

[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_isolation = 'read-committed'   # 建议项

transaction_write_set_extraction=XXHASH64         # 必须
loose-group_replication_group_name="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"  # 必须
loose-group_replication_enforce_update_everywhere_checks=ON  # 非必需,但强烈建议
loose-group_replication_single_primary_mode=OFF  # 必须,关闭单主模型,即使用多主
loose-group_replication_start_on_boot=off        # 建议设置为OFF
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,192.168.100.23:20003,192.168.100.24:20004,192.168.100.25:20005"

s2、s3、s4和s5节点的配置文件和s1类似,但server_idloose-group_replication_local_address必须改成各节点对应的值。

s2的配置(未包括和s1相同的配置):

server_id=110
loose-group_replication_local_address="192.168.100.22:20002"

s3的配置(未包括和s1相同的配置):

server_id=120
loose-group_replication_local_address="192.168.100.23:20003"

s4的配置(未包括和s1相同的配置):

server_id=130
loose-group_replication_local_address="192.168.100.24:20004"

s5的配置(未包括和s1相同的配置):

server_id=140
loose-group_replication_local_address="192.168.100.25:20005"

配置结束后,启动mysqld实例。

systemctl start mysqld

3.创建复制用户,并设置恢复通道"group_replication_recovery"。

我这里将s1作为组内的个节点。所以只需在s1上创建复制用户即可,以后其他节点加入组时,会将该操作复制走。

在s1上执行:

mysqlcreate user repl@'192.168.100.%' identified by 'P@ssword1!';
mysqlgrant replication slave on *.* to repl@'192.168.100.%';

设置恢复阶段的异步复制通道:

在s1上执行:

mysql> change master to 
            master_user='repl',
            master_password='P@ssword1!'
            for channel 'group_replication_recovery';

注意:后面的操作中,如果没有明确指定在s2、s3、s4和s5上执行,那么都是在s1上执行的。有些操作是不允许在多个节点上都执行的。

4.在s1上安装组复制插件,并引导创建复制组。

安装组复制插件,在s1上执行:

mysql> install plugin group_replication soname 'group_replication.so';

以s1节点组的引导节点,在s1上执行:

mysqlset @@global.group_replication_bootstrap_group=on;
mysqlstart group_replication;
mysqlset @@global.group_replication_bootstrap_group=off;

执行完上面的语句后,本实验所需的复制组就已经被节点s1创建了。以后s2-s5节点就可以陆续地加入到组中。

在其他节点加组之前,先看下组中的节点s1是否已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: s1.longshuai.com
 MEMBER_PORT: 3306
MEMBER_STATE: ONLINE

5.向组中加入新节点:s2、s3、s4、s5。

在s2、s3、s4和s5上都执行:

change master to 
            master_user='repl',
            master_password='P@ssword1!'
            for channel 'group_replication_recovery';

install plugin group_replication soname 'group_replication.so';

然后依次在s2、s3、s4和s5上执行下面的语句开启组复制功能,开启该功能后,将自动加入到组中。但注意,要依次执行,在每个start语句返回成功后再去下一个节点执行:

start group_replication;

6.查看组中成员s1、s2、s3、s4、s5是否全都ONLINE。

在任意一个节点上执行:

mysql> select from performance_schema.replication_group_members\G
**************************1. row ***************************
CHANNEL_NAME: group_replication_applier
   MEMBER_ID: 22e55db0-7604-11e8-b72d-000c29b06c3c
 MEMBER_HOST: s5.longshuai.com
 MEMBER_PORT: 3306
MEMBER_STATE: ONLINE
**************************2. row ***************************
CHANNEL_NAME: group_replication_applier
   MEMBER_ID: a5165443-6aec-11e8-a8f6-000c29827955
 MEMBER_HOST: s2.longshuai.com
 MEMBER_PORT: 3306
MEMBER_STATE: ONLINE
**************************3. row ***************************
CHANNEL_NAME: group_replication_applier
   MEMBER_ID: a659234f-6aea-11e8-a361-000c29ed4cf4
 MEMBER_HOST: s1.longshuai.com
 MEMBER_PORT: 3306
MEMBER_STATE: ONLINE
**************************4. row ***************************
CHANNEL_NAME: group_replication_applier
   MEMBER_ID: ba505889-6aec-11e8-a864-000c29b0bec4
 MEMBER_HOST: s3.longshuai.com
 MEMBER_PORT: 3306
MEMBER_STATE: ONLINE
**************************5. row ***************************
CHANNEL_NAME: group_replication_applier
   MEMBER_ID: bf12fe97-6aec-11e8-a909-000c29e55287
 MEMBER_HOST: s4.longshuai.com
 MEMBER_PORT: 3306
MEMBER_STATE: ONLINE
5 rows in set (0.00 sec)

测试多主模型的写负载

多主模型下,所有节点都可以进行读、写操作。但请注意,组复制的几个要求:表必须为innodb表(虽然创建myisam表不报错,但修改数据会报错)、每个表必须有主键、不能有级联的外键等。

在任意节点上执行以下写操作进行测试:

create database mut_gr;
create table mut_gr.t1(id int primary key);
insert into mut_gr.t1 values(1),(2),(3),(4);

在任意节点上继续执行写操作:

insert into mut_gr.t1 values(5);

查看数据是否都已同步到各节点上。

出处:cnblogs.com/f-ck-need-u/p/9215013.html




分享好友

分享这个小栈给你的朋友们,一起进步吧。

数据库专区
创建时间:2020-06-16 18:15:35
数据库是存放数据的仓库。它的存储空间很大,可以存放百万条、千万条、上亿条数据。但是数据库并不是随意地将数据进行存放,是有一定的规则的,否则查询的效率会很低。当今世界是一个充满着数据的互联网世界,充斥着大量的数据。即这个互联网世界就是数据世界。数据的来源有很多,比如出行记录、消费记录、浏览的网页、发送的消息等等。除了文本类型的数据,图像、音乐、声音都是数据。 [2]
展开
订阅须知

• 所有用户可根据关注领域订阅专区或所有专区

• 付费订阅:虚拟交易,一经交易不退款;若特殊情况,可3日内客服咨询

• 专区发布评论属默认订阅所评论专区(除付费小栈外)

技术专家

查看更多
  • 小雨滴
    专家
  • gaokeke123
    专家
戳我,来吐槽~