MySQL Group Replication 是 MySQL 的一种高可用解决方案。在 MGR 出现之前,高可用主要通过异步复制和半同步复制来实现,有了主从复制就一定程度上避免了单点故障。但是主从复制解决不了数据一致性、自动故障恢复等问题,同时维护比较复杂,只能借助一大堆工具或脚本来解决。MGR 的出现解决了数据一致性问题,同时内置了故障自动恢复。在 8.0 中,借助 MySQL InnoDB Cluster 框架,MGR 部署维护变得前所未有地简单。

部署工具

下面,我们通过 MySQL Shell 来部署 MGR,版本为 8.0.25。如果想尝试手动部署 MGR,可以参考 MySQL 官方文档。但我还是建议使用 MySQL Shell,因为真的很方便,几条命令就能部署起来一个集群。使用 MySQL Shell 部署 MGR,主要用到 dba 这个对象中的方法。它的所有方法如下图所示:

mysql-shell-dba-method

在这次部署集群的示例中,我们只需要 createClusteraddInstance 和方法,分别是创建集群和往集群添加实例。MySQL Shell 中可以用 PythonJavaScript 语法来执行这些方法,默认是 JavaScript 语法,下面我们也用 JavaScript 来举例。

部署文件

我准备了一个 docker-compose 文件来简单编排一下 mgr 集群实例,可以从 mgr-simple 获取。MGR 启动需要开启一些参数,在 docker-compose.yaml 中的 command 可以看到。这个示例中使用的是 3 节点单主架构。

binlog_transaction_dependency_tracking=WRITESET
enforce_gtid_consistency=ON --gtid_mode=ON
slave_parallel_type=LOGICAL_CLOCK
slave_preserve_commit_order=ON

因为 MySQL 8.0 默认密码验证插件改了,为了兼容 5.x 版本的客户端,所以这里设置了一下 default-authentication-plugin=mysql_native_password

启动实例

docker-compose.yaml 相同目录中执行 docker-compose up -d 启动三个实例。

1
2
3
4
5
6
7
8
9
$ docker-compose up -d
Creating network "mgr-simple_default" with the default driver
Pulling db-1 (mysql/mysql-server:8.0.25)...
8.0.25: Pulling from mysql/mysql-server
Digest: sha256:56ec3d7509327c66e4b8b22c72ecd56572ae1f87c91ef806c80fa09c7707c845
Status: Downloaded newer image for mysql/mysql-server:8.0.25
Creating mgr-simple_db-1_1 ... done
Creating mgr-simple_db-3_1 ... done
Creating mgr-simple_db-2_1 ... done

待容器创建完成后,执行 docker ps | grep mysql/mysql-server 查看刚才创建的容器。

1
2
3
4
$ docker ps | grep mysql/mysql-server
28923e9bd584   mysql/mysql-server:8.0.25   "/entrypoint.sh --de…"   13 minutes ago   Up 13 minutes (healthy)   33060-33061/tcp, 0.0.0.0:33061->3306/tcp, :::33061->3306/tcp   mgr-simple_db-1_1
ee9d0e4f0a0c   mysql/mysql-server:8.0.25   "/entrypoint.sh --de…"   13 minutes ago   Up 13 minutes (healthy)   33060-33061/tcp, 0.0.0.0:33062->3306/tcp, :::33062->3306/tcp   mgr-simple_db-2_1
4c28a75baf2b   mysql/mysql-server:8.0.25   "/entrypoint.sh --de…"   13 minutes ago   Up 13 minutes (healthy)   33060-33061/tcp, 0.0.0.0:33063->3306/tcp, :::33063->3306/tcp   mgr-simple_db-3_1

创建集群

登录其中一个上一步创建的容器中,在容器中进入到 MySQL Shell 环境,docker exec -it container_id bashcontainer_id 为上一步获取的容器 ID。MySQL Shell 进入的命令为 mysqlsh --uri user:password@ip:port,该示例中,我们密码是 123,同时在 docker-compose.yaml 中指定了 hostname,所以我们可以用 db-1 代替 ip。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
$ docker exec -it 28923e9bd584 bash
bash-4.4# mysqlsh --uri root:123@db-1:3306
Cannot set LC_ALL to locale en_US.UTF-8: No such file or directory
MySQL Shell 8.0.25

Copyright (c) 2016, 2021, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
Other names may be trademarks of their respective owners.

Type '\help' or '\?' for help; '\quit' to exit.
WARNING: Using a password on the command line interface can be insecure.
Creating a session to 'root@db-1:3306'
Fetching schema names for autocompletion... Press ^C to stop.
Your MySQL connection id is 47
Server version: 8.0.25 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.
 MySQL  db-1:3306 ssl  JS >

上面说到,MySQL Shell 默认的语法是 JS,所以我们输入的命令都是驼峰格式。退出 shell 可以用 \quit\q,进入 sql 环境可以用 \sql,返回到 API 环境中可以用 \js\py

进入到了 MySQL Shell,我们就可以创建集群了,命令为 dba.createCluster('Cluster'),必选参数为集群名称,可选参数是一个字典类型,很多其他命令也都类似,在最后一个参数中可以填可选参数,具体每个命令的可选参数可以查看文档。

不得不说这个彩色命令行真的很加分,看起来非常舒服。

从提示中,可以看到集群已经创建成功了,此时集群中只有一个节点,集群至少需要 3 个节点才能高可用,容忍一个节点异常。

这时我们可以使用 dba.getCluster().status() 查看集群状态了,可以发现集群中只有一个节点。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
> dba.getCluster().status()
{
    "clusterName": "Cluster",
    "defaultReplicaSet": {
        "name": "default",
        "primary": "db-1:3306",
        "ssl": "REQUIRED",
        "status": "OK_NO_TOLERANCE",
        "statusText": "Cluster is NOT tolerant to any failures.",
        "topology": {
            "db-1:3306": {
                "address": "db-1:3306",
                "memberRole": "PRIMARY",
                "mode": "R/W",
                "readReplicas": {},
                "replicationLag": null,
                "role": "HA",
                "status": "ONLINE",
                "version": "8.0.25"
            }
        },
        "topologyMode": "Single-Primary"
    },
    "groupInformationSourceMember": "db-1:3306"
}

添加节点

接着,我们把其他两个节点加入到集群中,使用 addInstance 方法,同时我们指定数据同步的方式为 clone,意为全量复制,集群重新恢复并加入到集群后,会全量同步集群中的数据到该节点中。数据恢复的方式还有增量这种方式,我们可以根据不同情况使用。

1
2
dba.getCluster().addInstance('root:123@db-2:3306', {'recoveryMethod': 'clone'})
dba.getCluster().addInstance('root:123@db-3:3306', {'recoveryMethod': 'clone'})

添加完其他两个节点后,再来查看一下集群状态,3 个节点都出现了,集群状态正常。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
> dba.getCluster().status()
{
    "clusterName": "Cluster",
    "defaultReplicaSet": {
        "name": "default",
        "primary": "db-1:3306",
        "ssl": "REQUIRED",
        "status": "OK",
        "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
        "topology": {
            "db-1:3306": {
                "address": "db-1:3306",
                "memberRole": "PRIMARY",
                "mode": "R/W",
                "readReplicas": {},
                "replicationLag": null,
                "role": "HA",
                "status": "ONLINE",
                "version": "8.0.25"
            },
            "db-2:3306": {
                "address": "db-2:3306",
                "memberRole": "SECONDARY",
                "mode": "R/O",
                "readReplicas": {},
                "replicationLag": null,
                "role": "HA",
                "status": "ONLINE",
                "version": "8.0.25"
            },
            "db-3:3306": {
                "address": "db-3:3306",
                "memberRole": "SECONDARY",
                "mode": "R/O",
                "readReplicas": {},
                "replicationLag": null,
                "role": "HA",
                "status": "ONLINE",
                "version": "8.0.25"
            }
        },
        "topologyMode": "Single-Primary"
    },
    "groupInformationSourceMember": "db-1:3306"
}

卸载

docker-compose.yaml 相同目录中执行 docker-compose down

小结

至此,MGR 集群就部署完成了,借助 MySQL Shell,极大便利了 MGR 的运维,创建集群只需要简单的几个命令。这也是 MySQL 高可用发展的一个趋势。除了部署,使用 MySQL Shell 还可以很多玩法来管理 MGR 集群。

参考资料