MySQL高可用架构-MGR(下)(mysql高可用keepalived)

关注我「程序猿集锦」,获取更多分享。

前置阅读:MySQL高可用架构-MGR(上)MySQL高可用架构-MGR(中)

  • 单主和多主模式的转换
    • 单主模式转换为多主模式
    • 多主模式转换为单主模式
  • MGR高可用集群拓扑图
    • 单主模式
    • 多主模式
  • MGR候选主节点规则
  • 集群节点状态
  • MGR集群节点容错关系
  • 错误问题

单主和多主模式的转换

我们知道MGR有两种工作模式:单主模式和多主模式。单主模式下,只要一个主节点可以提供写的服务,其他从节点值提供读的服务;多主模式下,部分主和从,任何一个节点都可以提供读和写。

我们在前面的实验中,搭建了单主模式的MGR集群。现在我们把这个单主模式的集群转换为多主模式,然后再多主模式转换为单主模式。

单主模式转换为多主模式

  • 把所有节点上面的MGR复制功能都关闭掉,在每一个节点上面,都执行如下命令:
stop group_replication;
  • 在所有节点上面执行如下命令,用于关闭该节点上面单主模式的开关,如果要永久生效记得修改每一个节点的my.cnf配置文件中对应的这个参数的值。
set global group_replication_single_primary_mode = off;
  • 选择一个节点作为多主模式MGR集群的初始化节点,执行如下命令:
set global group_replication_bootstrap_group = on;
start group_replication;
set global group_replication_bootstrap_group = off;
  • 在剩余的其他节点上,都执行如下命令,用于加入到多主模式的MGR集群中。
start group_replicaiton;
  • 检查每一个节点的读写状态,所有的节点都是off的配置才对。
show variables like '%read_only%';

下面给出我们把一种三从的单主MGR集群转换为多主MGR集群的过程:

在master1、master2、master3、master4上都停止MGR集群的复制功能,并关闭单主模式,如下所示:

mysql> stop group_replication;
Query OK, 0 rows affected (9.53 sec)

mysql> select @@hostname;
+---------------+
| @@hostname    |
+---------------+
| master1.mysql |
+---------------+
1 row in set (0.00 sec)

mysql> set global group_replication_single_primary_mode = off;
Query OK, 0 rows affected (0.00 sec)
mysql> stop group_replication;
Query OK, 0 rows affected (9.53 sec)

mysql> select @@hostname;
+---------------+
| @@hostname    |
+---------------+
| master2.mysql |
+---------------+
1 row in set (0.00 sec)

mysql> set global group_replication_single_primary_mode = off;
Query OK, 0 rows affected (0.00 sec)
mysql> stop group_replication;
Query OK, 0 rows affected (9.53 sec)

mysql> select @@hostname;
+---------------+
| @@hostname    |
+---------------+
| master3.mysql |
+---------------+
1 row in set (0.00 sec)

mysql> set global group_replication_single_primary_mode = off;
Query OK, 0 rows affected (0.00 sec)
mysql> stop group_replication;
Query OK, 0 rows affected (9.53 sec)

mysql> select @@hostname;
+---------------+
| @@hostname    |
+---------------+
| master4.mysql |
+---------------+
1 row in set (0.00 sec)

mysql> set global group_replication_single_primary_mode = off;
Query OK, 0 rows affected (0.00 sec)

我们随机选择一个节点作为多主MGR集群的初始化节点,我们这里选择master2节点。在master2节点上执行如下命令初始化MGR集群,初始化之后,可以看到master2节点是可以提供读写服务的。

mysql> set global group_replication_bootstrap_group = on;
Query OK, 0 rows affected (0.00 sec)

mysql> start group_replication;
Query OK, 0 rows affected (2.05 sec)

mysql> set global group_replication_bootstrap_group = off;
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like '%read_only%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| innodb_read_only      | OFF   |
| read_only             | OFF   |
| super_read_only       | OFF   |
| transaction_read_only | OFF   |
| tx_read_only          | OFF   |
+-----------------------+-------+
5 rows in set (0.02 sec)

mysql> select @@hostname;
+---------------+
| @@hostname    |
+---------------+
| master2.mysql |
+---------------+
1 row in set (0.01 sec)

mysql>

然后再master1、master3、master4三个节点上面分别执行如下命令,让各个节点加入到MGR集群中去,同时可以查看各个节点的read_only属性的值都是off,也就是这些节点都是可以提供读写服务的。

mysql> start group_replication;
Query OK, 0 rows affected (6.12 sec)

mysql> show variables like '%read_only%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| innodb_read_only      | OFF   |
| read_only             | OFF   |
| super_read_only       | OFF   |
| transaction_read_only | OFF   |
| tx_read_only          | OFF   |
+-----------------------+-------+
5 rows in set (0.01 sec)

mysql> select @@hostname;
+---------------+
| @@hostname    |
+---------------+
| master1.mysql |
+---------------+
1 row in set (0.00 sec)

mysql>
mysql> start group_replication;
Query OK, 0 rows affected (3.10 sec)

mysql> show variables like '%read_only%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| innodb_read_only      | OFF   |
| read_only             | OFF   |
| super_read_only       | OFF   |
| transaction_read_only | OFF   |
| tx_read_only          | OFF   |
+-----------------------+-------+
5 rows in set (0.01 sec)

mysql> select @@hostname;
+---------------+
| @@hostname    |
+---------------+
| master3.mysql |
+---------------+
1 row in set (0.00 sec)

mysql>
mysql> start group_replication;
Query OK, 0 rows affected (3.08 sec)

mysql> show variables like '%read_only%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| innodb_read_only      | OFF   |
| read_only             | OFF   |
| super_read_only       | OFF   |
| transaction_read_only | OFF   |
| tx_read_only          | OFF   |
+-----------------------+-------+
5 rows in set (0.01 sec)

mysql> select @@hostname;
+---------------+
| @@hostname    |
+---------------+
| master4.mysql |
+---------------+
1 row in set (0.00 sec)

mysql>

此时的单主MGR集群就被我们给转换为多主MGR集群了,最后的四个节点如下所示:

mysql> select * from performance_schema.replication_group_members;
+---------------------------+--------------------------------------+---------------+-------------+--------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST   | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+---------------+-------------+--------------+
| group_replication_applier | 8c9421e4-86c2-11eb-a34b-0242ac16000b | master1.mysql |        3306 | ONLINE       |
| group_replication_applier | dec71d97-86c2-11eb-b5b3-0242ac160016 | master2.mysql |        3306 | ONLINE       |
| group_replication_applier | e7561c37-86c2-11eb-b4ce-0242ac160021 | master3.mysql |        3306 | ONLINE       |
| group_replication_applier | 7fdaaff9-86c5-11eb-b807-0242ac16002c | master4.mysql |        3306 | ONLINE       |
+---------------------------+--------------------------------------+---------------+-------------+--------------+
4 rows in set (0.00 sec)

mysql>

多主模式转换为单主模式

现在,我们把多主的MGR集群转换为单主模式的MGR集群。具体操作步骤如下:

  • 在所有节点,都执行如下命令,用于关闭MGR组复制功能:
stop group_replication;
  • 在所有节点,都执行如下命令,用于开启单主模式,如果要永久生效记得修改每一个节点的my.cnf配置文件中对应的这个参数的值。
set global group_replication_single_primary_mode = on;
  • 选择一个节点作为MGR集群的初始化节点,也就是单主MGR集群的主节点,执行如下命令:
set global group_replication_bootstrap_group = on;
start group_replication;
set global group_replication_bootstrap_group = off;
  • 在剩余的节点上,执行如下命令,让其加入到单主MGR集群中:
start group_replication;
  • 检查每一个节点的读写状态,只有在主节点中,才是off的配置,其他节点都应该是on
show variables like '%read_only%';

具体转换的过程,这里就不再贴出执行过程的输出了。

注意:上面单主转多主或多主转单主的过程,我们只是在命令窗口对相关参数进行的修改,如果要使修改永久生效,记得把对应每一个节点的my.cnf配置文件也作出对应的修改,避免MySQL数据库重启后失效,以便永久生效。

MGR高可用集群拓扑图

单主模式

前面我们已经搭建了MGR的集群,但是我们想要达到高可用,还需要一些东西:VIP。就是当我们的单主模式下面的主节点宕机之后,我们会有其他从节点顶替上来称为新的主节点,此时我们的应用程序并不知道这个操作,应用该如何去连接到新的主节点呢?此时就需要用到数据库中间件和VIP的控制。如下就是MGR高可用集群的一个网络拓扑:


下面针对这个网络拓扑做简要说明:

  • MGR集群目前最大支持9个节点,所以上图中最多到master9节点。
  • 在master1和master2两个节点上面部署了keepalive组件,为了是在单主模式下当master1宕机之后,VIP可以从master1上漂移到master2上面。这样应用可以通过VIP来连接MGR单主集群。
  • 为了能够让master2节点一定成为候选主节点,则需要配置group_replication_member_weight参数的值大于集群中其他节点的值,并且和master1节点的值相同。让其在master1宕机之后能够优先成为主节点,同样在master2宕机之后,master1可以优先成为主节点。比如配置master1和master2节点上这个参数的值为90,其他节点的值保存默认50。所以我们只要在master1和master2上面部署keepalive,保证让VIP在这两个节点上面来回漂移即可。
  • 除了上面值选择两个节点作为单主模式下面的主之外,也可以在3个或更多个节点上面都部署keepalive,让VIP在这些节点上面有规律的进行漂移。在keepalive的配置中,也有相关权重的参数控制那些节点可以优先获得VIP,然后再结合MGR中的参数group_replication_member_weight也可以做到VIP会漂移到真正的候选主节点上。
  • 在向上层是部署的两个mycat数据库中间件,为了做高可用,这里也部署了两台mycat服务。并且在这两台上面都部署了keepalive服务,与此同时,这里也绑定了一个VIP,用于在两台mycat服务之间来回漂移。
  • 在mycat中,我们配置只有vip可以提供读写,所有节点真实的IP都配置为只读节点即可。
  • 我们的应用层,在连接数据库的时候,是配置的mycat上的VIP,通过这个VIP来连接MySQL数据库的MGR集群。

多主模式

对于多主模式下,相对于单主模式,它的网络图片如下所示:


针对如上的多主模式的MGR高可用集群网络拓扑图,简单做如下的说明:

  • 和单主模式相比,此时的多主MGR高可用集群,不用维护集群内的VIP了,因为所有的节点都可以提供读写服务。所以我们不用在意到底是哪个节点提供写的服务。
  • 我们在mycat的配置文件中,把所有节点都配置为可读写就可以了。
  • MySQL为了高可用,也部署两个。通过VIP对应用层来提供服务。

MGR候选主节点规则

在MGR单主模式下,如果主挂掉后,会从众多的从节点中,选举一个节点作为新的主,到底是根据什么规则来选择新的主节点呢?这个选择结果受到多个因素的影响。包括:MySQL的版本、
group_replication_member_weight
参数的值、server_uuid的值。

  • 在MGR集群中各个节点的MySQL版本一致的情况下,首选会根据参数group_replication_member_weight值的大小来选举新的master节点。这个参数取值范围为[0,100],默认值为50,是从5.7.20版本才引进的。该参数的值越大的节点,越优先被选择为新的主节点。
  • 在MGR集群中各个节点的MySQL版本一致,并且所有节点的group_replication_member_weight参数配置的都相同的情况下,会根据每一个节点的server_uuid值的大小来决定选择哪个节点为主节点,server_uuid值越小的节点,越优先被选择为主节点。
  • 在MGR集群中各个节点的MySQL版本不一致的情况下,因为主从复制有一个大的前提是:主节点版本不得高于从节点的版本。所以会优先选择MySQL版本较低的节点作为主节点。
  • 如果集群中有不支持group_replication_member_weight参数的节点存在,也就是节点中MySQL的版本低于5.7.20,则会选择版本最低的那个节点作为新的主节点。如果存在多个相同低版本的MySQL节点,再根据这些低版本MySQL节点的server_uuid来选择新的主节点。server_uuid最小的那个被优先选择为新的主节点。
  • 如果集群中的MySQL节点都支持group_replication_member_weight参数,也就说所有节点的MySQL版本都高于5.7.20,则会选择MySQL版本最低的那个节点作为主节点。如果存在多个相同低版本的MySQL节点,则会根据各个节点的参数group_replication_member_weight的值来选择主节点,该参数值越大的越优先被选为新的主节点。

综上所述,在选择新的主节点的时候,采用的优先级别为:MySQL的版本 >
group_replication_member_weight > server_uuid

  • 先看MySQL的版本,选择版本最低的节点作为新的主节点。如果不是只有一个低版本的MySQL节点,存在多个低版本的MySQL节点,也是参考如下两种情况进行选择新的主。
  • 如果MySQL的版本相同,再看版本是否支持参数group_replication_member_weight,如果支持这个参数,就根据这个参数的值来选择新的主节点。该参数值越大,越优先被选择为新的主节点,如果group_replication_member_weight参数的值相同,再看具有相同group_replication_member_weight值的节点的server_uuid的值,server_uuid的值越小越优先被选择为新的主。
  • 如果MySQL的版本相同,再看版本是否支持参数group_replication_member_weight,如果不支持这个参数,就根据各个节点的server_uuid的值来选择新的主,server_uuid的值越小越优先被选择为新的主。

延伸:如何查看MySQL的版本、server_id和参数
group_replication_member_weight

MySQL的server_uuid的值可以在data_dir目录下面的auto.cnf配置文件中查看。在MySQL启动的时候会检查这个目录下面是否有auto.cnf配置文件,如果没有则自动生成一个这样的配置文件,如果有则用已经存在的配置文件。这个文件中只有一个server_uuid的配置,如下所示:

root@master2:/var/lib/mysql# more auto.cnf
[auto]
server-uuid=dec71d97-86c2-11eb-b5b3-0242ac160016
root@master2:/var/lib/mysql# pwd
/var/lib/mysql
root@master2:/var/lib/mysql#

通过SQL命令查看MySQL数据库版本、参数server_uuid
group_replication_member_weight
,参考如下:

mysql> select version();
+------------+
| version()  |
+------------+
| 5.7.31-log |
+------------+
1 row in set (0.00 sec)

mysql> show variables like 'server_uuid';
+---------------+--------------------------------------+
| Variable_name | Value                                |
+---------------+--------------------------------------+
| server_uuid   | dec71d97-86c2-11eb-b5b3-0242ac160016 |
+---------------+--------------------------------------+
1 row in set (0.06 sec)

mysql> show global variables like 'group_replication_member_weight';
+---------------------------------+-------+
| Variable_name                   | Value |
+---------------------------------+-------+
| group_replication_member_weight | 50    |
+---------------------------------+-------+
1 row in set (0.01 sec)

mysql>

集群节点状态

MGR集群各个节点的状态有以下几种情况:

状态

状态含义

集群中的数据是否同步

ONLINE

节点的状态正常。

Yes

RECOVERING

节点正在从一个镜像节点拉取数据,在同步数据的过程中。

No

OFFLINE

节点下线了,不属于任何MGR集群。

No

ERROR

执行失败的时候,就会进入这个状态。

No

UNREACHABLE

当镜像节点不能正常连接的时候,会出现这个状态,一般是网络不通导致的。

No

MGR集群节点容错关系

MGR集群中使用的是Paxos分布式算法来保证集群的高可用,要求集群中的节点数要达到一定的数量才可以正常运行。这个数量的要求是及其中大多数节点可用则集群就可用,这个大多数是指n/2+ 1,其中的n表示集群中总节点数目。

结合上面给出的公式,MGR集群节点数目与容错节点之间的关系

节点总数目

最少需要存活节点数目

最多允许失败的节点数目

1

1

0

2

2

0

3

2

1

4

3

1

5

3

2

6

4

2

7

4

3

8

5

3

9

5

4

错误问题

MySQL的错误日志如下,错误日志文件默认为/var/log/mysql/error.log。这个错误的大致原因是你要加入到集群中的这个MySQL节点,比当前MGR集群中的的节点的GTID的值要大。也就是说你的这个节点中有一些数据已经存在或提交的事务数量,比集群中的节点要多,

2021-03-10T05:28:16.075508Z 0 [ERROR] Plugin group_replication reported: 'This member has more executed transactions than those present in the group. Local transactions: fbf6a9d9-8148-11eb-b254-0242ac160016:1-5 > Group transactions: 0b7fac73-0413-41bf-b197-210da2a5c024:1-11,
fba2d042-8148-11eb-89c9-0242ac16000b:1-5,
fc5d2e7a-8148-11eb-a6f7-0242ac160021:1-5'
2021-03-10T05:28:16.076012Z 0 [Warning] Plugin group_replication reported: 'The member contains transactions not present in the group. It is only allowed to join due to group_replication_allow_local_disjoint_gtids_join option'

如果你确定要用集群中节点的数据为准,那么可以使用下面的解决方式来解决,设置这个参数之后,然后再次尝试启动组复制功能。

set global group_replication_allow_local_disjoint_gtids_join=on;

白名单的问题,需要在配置文件中配置好,然后重启MySQL服务。

mysql> set global group_replication_ip_whitelist = '172.22.0.1/24';
ERROR 3093 (HY000): The IP whitelist cannot be set while Group Replication is running
mysql>


到此,关于MySQL高可用机构之MGR的相关介绍就介绍了,如果你有什么疑问可以在评论区留言讨论。