第21章 组复制

目录

21.1 组复制背景
21.1.1 复制技术
21.1.2 组复制案例
21.1.3 组复制详细说明
21.2 开始入门
21.2.1 单主模式下部署组复制
21.3 监控组复制
21.3.1 Replication_group_member_stats
21.3.2 Replication_group_members
21.3.3 Replication_connection_status
21.3.4 Replication_applier_status
21.3.5 组复制服务器状态
21.4 组复制操作
21.4.1 多主或主模式下部署组
21.4.2 调优恢复
21.4.3 网络分区
21.5 组复制安全
21.5.1 IP 地址白名单
21.5.2 安全套件层协议支持(SSL)
21.5.3 虚拟私有网络(VPN)
21.6 组复制系统变量
21.7 要求和局限性
21.7.1 组复制要求
21.7.2 组复制局限性
21.8 常见问答
21.9 组复制技术细节
21.9.1 组复制插件架构
21.9.2 组
21.9.3 数据操作语句
21.9.4 数据定义语句
21.9.5 分布式恢复
21.9.6 可观测性
21.9.7 组复制性能

本章将解释MySQL组复制以及如何安装、配置和监控组复制。MySQL组复制是一个MySQL服务器插件,它使您能够创建易伸缩的、高可用性、 容错的复制拓扑架构。

组可以在单主模式下进行自动选主,其中只有一个服务器一次接受更新。或者,对于更高需求的用户,组可以部署在多主模式中,所有服务器都可以接受更新,即便它们是并发执行

有一个内置的组成员关系服务,它可以使组的视图保持一致,并在任何给定的时间点对所有服务器可用。服务器可以离开或加入组,视图也会相应更新。有时,服务器会意外地离开组,在这种情况下,故障检测机制会检测到这一点,并通知组视图已经发生了变化。这些都是自动的。

本章结构如下:

21.1 组复制背景

本节提供MySQL组复制的背景信息。

最常见的容灾系统就是使用组件冗余,换句话说组件可以被删除,但是系统应该还是继续按预期运行。这样就产生了一些列的挑战,因为这些系统的复杂性提升到一个完全不同的水平。特别是数据库必须处理的一个事实,即,它们需要维护和管理多个服务器,而不是一个服务器。此外,由于服务器在一起进行协作,产生了一些需要处理的经典分布式系统问题,如网络分区和脑裂情景。

因此,最终的挑战是将数据库和数据流的逻辑,多个服务器协调的逻辑,用一致的简单的方式融合在一起。一句话,就是要让让多个服务器在系统状态和每一个变化的数据达到一致。这可以总结为,让服务器在每个数据库状态转换上都达成一致。这样它们就像是单个数据运行,或者它们最终聚合到相同的状态。 这就意味着它们需要以一种(分布式)状态操作。

MySQL 组复制提供了分布式状态机的复制,服务器之间的协调性很强。当服务器属于相同组的一部分时,服务器自身会自动协调。组可以在单主模式下自动选主,其中每次只有一台服务器接受更新,另外,对于更高要求的用户,可以在多主模式下部署组,这时,所有的服务器都可以接受更新,即便是并发的发送过来。当然,这种能力是以牺牲应用程序为代价的,因为应用程序必须解决这种部署所带来的限制。

有个内置的组员关系服务,用以确保在任何给定的时间点,组的视图对所有的服务器都是一致的及可用的。服务器节点可以脱离或假如组,并且会更新相应的视图,有时服务器节点可能会意外的脱离组,在这种情况下,故障检测机制会检测到,并通知组视图已发生变化。这些都是自动的。

要提交一个事务时, 大部分组员必须在全局事务序列中,就这个给定的事务顺序达成一致。决定提交或中止事务是由每个服务器单独完成,但所有的服务器成员必须做出相同的决定。如果存在一个网络分区,导致成员无法达成一致,那么系统不会继续进行,直到此问题解决。因此,还有一个内置的、自动的脑裂保护机制。

上述所有功能都是由组通信协议提供的。它们提供了故障检测机制、组成员服务,以及安全且完全有序的消息传递。所有这些属性都是创建系统的关键,确保数据在整个服务器组中得到一致性的复制。该技术的核心在于Paxos算法的实现。它充当着组通信系统引擎

21.1.1 复制技术

在进入详细介绍MySQL组复制前,本节先介绍一些背景概念和它们如何工作的概述。内容会提供一些内容来帮助理解组复制的需求,以及组复制和传统主从复制的不同之处。

21.1.1.1 主从复制

传统的MySQL复制提供了一种简单的主-从复制架构。其中一个主节点(master)和一个或多个从节点(slave),主节点执行事务,提供事务,然后这些事务随后(因此异步的)传送给从节点,从节点重新执行(语句模式的复制)或者应用(row模式的复制)。这是一个 shared-nothing系统,默认情况下所有的服务器都有一个完整的数据副本。

图 21.1 MySQL 异步复制

MySQL Asynchronous Replication

同时还有半同步复制,向异步复制协议添加一个同步步骤。这意味着主节点在提交时,需要等待从节点发出已 接收 到事务的通知。只有这样,主节点才继续提交操作。

图 21.2 MySQL 半同步复制

MySQL Semisynchronous Replication

上面的两个图中, 您可以看到一个经典的异步MySQL复制协议(以及它的半同步变体)的图。蓝色箭头表示服务器之间交换的消息或服务器和客户端应用程序之间交换的消息。

21.1.1.2 组复制

组复制是一种可用于实现容错系统的技术。复制组是一个通过消息传递相互交互的服务器组。通信层提供了很多保证,例如原子消息和总消息序号的传递。通过这些强大的特性,我们可以构建更高级的数据库复制解决方案。

MySQL组复制构建在这些属性和抽象之上,并实现多主复制协议的更新。实质上,复制组由多个数据库实例组成,并且组中的每个实例都可以独立地执行事务。但是所有读写(RW)事务只有在被组批准后才会提交。只读(RO)事务不需要在组内协调,因此立即提交。换句话说,对于任何RW事务,组需要决定是否提交,因此提交操作不是来自始发服务器的单向决定。准确地说,当事务准备好在始发服务器上提交时,该始发服务器原子地广播写入值(已改变的行)和对应的写入集(已更新的行的唯一标识符)。然后为该事务建立一个全局总序号。最终,这意味着所有服务器以相同的顺序接收同一组事务。因此,所有服务器以相同的顺序应用相同的一组更改,因此它们在组内保持一致。

但是,在不同服务器上并发执行的事务之间可能存在冲突。通过检查两个不同的并发事务的写集合来检测这样的冲突,这个检查过程称为认证( certification)。如果在不同的服务器上执行的两个并发事务更新同一行,则会出现冲突。解析过程会这么做,首先发起的事务在所有服务器上提交,而后发起的事务将在源服务器上回滚,并由组中的其他服务器删除。这实际上是一个分布式环境下“优先提交者赢”的规则。

图21.3 MySQL 组复制协议

MySQL Group Replication Protocol

最后,组复制是一个shared-nothing复制方案,就是每个服务器自己都有整个数据的副本。

上图描述了MySQL组复制协议,并通过将其与MySQL复制(或甚至MySQL半同步复制)进行比较,您可以看到一些差异。注意,为了清楚起见,这个图片中缺少一些基本的共识和Paxos相关信息。

21.1.2 组复制使用案例

组复制使您能够通过在一组服务器中复制系统状态来创建具有冗余的容错系统。因此,即使一些服务器发生故障,只要它不是全部或大多数,虽然可能降低系统性能或可扩展性,但系统仍然可用。单一服务器故障是隔离和独立的。它们由组员关系服务跟踪,它依赖于分布式故障检测器,分布式故障检测器能够在任何节点成员自愿地或由于意外停止而离开群组时发出信号。分布式恢复过程能够确保当有新节点加入组时,该节点会自动更新到最新。Multi-master特性确保即使在单个服务器故障的情况下也不会阻止更新。因此,MySQL组复制保证数据库服务持续可用。

不过需要重点理解,尽管组复制存在高可用,但连接到它的客户端必须被重定向或故障转移到不同的服务器。这不是组复制尝试解决的问题,而是连接器,负载均衡器,路由器或一些其他中间件更适合处理这个问题。

总而言之,MySQL组复制提供了高可用性、高弹性、可靠的MySQL服务。

21.1.2.1 使用场景案例

下面的案例就是组复制的经典用法。

  • 弹性复制 - 需要非常流畅的复制基础架构的环境,其中服务器的数量必须动态增长或收缩,尽可能减少副作用。例如,云的数据库服务。

  • 高可用分片 - 分片是实现写扩展的常用方法。使用MySQL组复制实现高可用性分片,其中每个分片映射到复制组。

  • 替代主从复制 - 在某些情况下,使用单主节点可能使其成为单点争用。在某些情况下,写入整个组可能更具可扩展性。

  • 自动化系统 - 可以将MySQL组复制当做一个纯粹的自动化复制系统(在本节和前面章节已经描述过)。

21.1.3 组复制详解

本节将介绍组复制所构建的一些服务的详细信息。

21.1.3.1 故障检测

组复制提供了一种故障检测机制,其能够找到和报告哪些服务器是没有响应的,并假定其是死的。更深入地说,故障检测器是提供关于哪些服务器可能已死的信息的分布式服务。后续如果组同意怀疑可能是真的,则由组来决定该服务器确实已经失败。这意味着组中的其余成员进行协调决定排除给定成员。

服务器无响应时触发怀疑机制。当服务器A在给定时间段内没有从服务器B接收消息时,发生超时就会引起怀疑。

如果服务器与组的其余部分隔离,则它怀疑所有其他服务器都失败。由于无法与组达成协议(因为它无法获得大多数成员认可),因此其怀疑没有后果。当服务器以此方式与组隔离时,它无法执行任何本地事务。

21.1.3.2 组员关系

MySQL组复制依赖于“组员关系服务”。这是内置的插件。它定义哪些服务器是在线的并参与在复制组中。在线服务器列表通常称为视图view。因此,组中的每个服务器具有一致的视图,其是在给定时刻积极参与到组中的成员。

复制组的成员们不仅需要同意事务是否提交,而且也需要决定当前视图。因此,如果服务器同意新的服务器成为组的一部分,则该组本身被重新配置以将该服务器集成在其中,从而触发视图改变。相反的情况也发生,如果服务器自愿地离开组,则该组动态地重新布置其配置,并且触发视图改变。

注意,当成员自愿离开时,它首先启动动态组重新配置。这触发一个过程,所有成员必须同意新的视图(也就是新视图中没有该离开成员)。然而,如果成员不由自主地离开(例如它已意外停止或网络连接断开),则故障检测机制实现这一事实,并且提出该组的重新配置(新视图没有该离开成员)。如上所述,这需要来自组中大多数成员的同意。如果组不能够达成一致(例如,以不存在大多数服务器在线的方式进行分区),则系统不能动态地改变配置,从而阻止脑裂情况引起的多写。最终,这意味着管理员需要介入并解决这个问题。

21.1.3.3 容错机制

MySQL组复制构建在Paxos分布式算法的实现上,以提供服务器之间的分布式协调。因此,它需要大多数服务器处于活动状态以达到选举条件,从而做出决定。这对系统可以容忍的故障数量有直接影响,一个组复制中成员数量(n),和允许故障的数量f, 则n = 2 x f + 1

在实践中,这意味着为了容忍一个故障,组必须有三个服务器。因此,如果一个服务器故障,仍然有两个服务器形成大多数(三分之二)并且允许系统自动地继续运行。但是,如果第二个服务器也异常失败,则该组(剩下一个服务器)阻塞,因为没有多数可以做出决定。

以下是说明上述公式的小表。

组员数量

大多数

允许故障数

1

1

0

2

2

0

3

2

1

4

3

1

5

3

2

6

4

2

7

4

3

下一节将涵盖实现组复制的技术。

21.2 入门

MySQL组复制是作为MySQL服务器的插件提供的,每个组中的每个服务器都需要配置和安装插件。本节提供了一个详细的教程,介绍了创建至少三个服务器的复制组所需的步骤。

21.2.1 单主模式下部署组复制

组中的每个服务器实例可以在独立的物理机器上运行,也可以在同一台机器上运行。本节解释如何在一台物理机器上创建一个包含三个MySQL服务器实例的复制组。这意味着需要三个数据目录,每个服务器实例一个目录,并且需要独立地配置每个实例。

图 21.4 组架构

Three servers deployed in a group

本教程讲解如何使用组复制插件来获取和部署MySQL服务器,如何在创建组之前配置每个服务器实例,以及如何使用Performance Schema监控来验证一切正常工作。

21.2.1.1 为组复制部署实例

第一步就是部署三个MySQL服务器实例。从MySQL 5.7.17开始,组复制就内置在MySQL插件中。更多关于MySQL插件的背景信息,参阅 5.6, “MySQL服务器插件”。下面假设MySQL被下载和解压到名为 mysql-5.7的目录。下面的步骤是使用一台物理机,因此每个MySQL服务实例都需要一个特定的数据目录。然后在目录创建名为 data的数据目录,并初始化每个数据目录。

mkdir data
mysql-5.7/bin/mysqld --initialize-insecure --basedir=$PWD/mysql-5.7 --datadir=$PWD/data/s1
mysql-5.7/bin/mysqld --initialize-insecure --basedir=$PWD/mysql-5.7 --datadir=$PWD/data/s2
mysql-5.7/bin/mysqld --initialize-insecure --basedir=$PWD/mysql-5.7 --datadir=$PWD/data/s3

data/s1, data/s2, data/s3 是初始化的数据目录,包含了MySQL系统库和相关的表等等。关于初始化流程的信息,参阅 2.9.1.1, “手动使用mysqld初始化数据目录”

警告

不要在生产环境使用--initialize-insecure,这里只是使用它来简化教程。更多关于安全设置的信息,参阅 21.5, “组复制安全”

21.2.1.2 为组复制配置实例

本节介绍对用于组复制的MySQL服务器实例的必要配置。

组复制服务器设置

为了安装和使用组复制插件,您必须正确的配置MySQL服务器实例。通常推荐将配置保存在实例的配置文件中,参阅 4.2.6, “使用选项文件”获取更多信息。除非另有说明,否则下面的配置都在组的第一个实例上进行,在这个过程中被称为s1。下面举例展示配置步骤。

[mysqld]

# server configuration
datadir=<full_path_to_data>/data/s1
basedir=<full_path_to_bin>/mysql-5.7/

port=24801
socket=<full_path_to_sock_dir>/s1.sock

这些设置是配置MySQL服务器,以使用前面创建的数据目录,以及打开服务器的端口,并开始监听连接。

注意

使用24801这个非默认端口,是因为在本教程中,三个服务器实例使用相同的主机名。如果在安装了三台不同机器的情况下,就可以不用考虑这个。

复制框架

以下设置根据MySQL组复制需求配置复制。

server_id=1
gtid_mode=ON
enforce_gtid_consistency=ON
master_info_repository=TABLE
relay_log_info_repository=TABLE
binlog_checksum=NONE
log_slave_updates=ON
log_bin=binlog
binlog_format=ROW

设置中将服务器配置唯一编号1,以便启用全局事务标识符,并将复制元数据存储在系统表中而不是文件中。此外,开启并使用row格式的二进制日志,禁用二进制日志事件校验和。更多详细内容,参阅 21.7.2 节, “组复制局限性”

组复制设置

此时,将配置选项保存在 my.cnf文件,来确保服务器的配置已生效,且在给定的配置选项下实例化复制的基础架构。 下面的内容就是为服务器实例配置组复制设置项。

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_local_address= "127.0.0.1:24901"
loose-group_replication_group_seeds= "127.0.0.1:24901,127.0.0.1:24902,127.0.0.1:24903"
loose-group_replication_bootstrap_group= off
注意

loose-前缀用于集群复制变量,如果在服务器启动时,没有加载组复制插件,则指示服务器继续启动。

  • 第1行 指示服务器,对于每个事务,它必须收集写集,并使用XXHASH64散列算法将其编码为散列。

  • 第2行 告诉插件,连接或创建组 "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"。

  • 第3行 指示插件在服务器启动时自动启动。

  • 第4行 告诉插件对于来自组内的其他成员的连接。使用 IP 地址 127.0.0.1, 或 localhost, 和端口 24901。

    重要

    这个服务器端口是用于组内成员之间的连接,这个端口不能给应用程序使用,她必须预留给组复制的成员使用。

    本地地址由 group_replication_local_address 配置,必须能被所有的组成员访问。举个例子,假如每个服务器实例在不同的机器上使用IP和端口,如: 10.0.0.1:33061。推荐端口号是33061,但是,在本教程中,因为我们的三个实例在一台机器上,所以我们使用了 24901到24903。

  • 第5行 告诉插件,如果有成员加入组,那么可以通过列举的主机和端口信息联系成员。这些是种子成员,当这些成员想连接到组时,就会使用这些,首先会连接其中的一个,然后它会请求组重新进行配置,以边让加入者在组中被接受。注意,这个选项不需要列出组中所有的成员,但是有一个服务器联系列表,以便服务器加入组。

    组中的第一台服务器启动组时,不需要使用到这个选项。因为它是初始服务器,负责引导组。第二台服务器连接请求加入组,询问该组中唯一的一个成员,然后组就扩展了。第三台服务器加入,会询问两个中的任意一个。随后的服务器加入,依次重复这个过程。

    警告

    当多个服务器同时加入时,确保它们指向了已在组中种子成员。不要将现在要加入组的成员作为种子成员,因为他们在连接的时候可能还不在组中。

    最好是先启动引导成员,让它创建组。然后让它成为需要加入组成员的种子成员。这样可以确保在加入其他成员时形成一个组。

    创建组,然后同时加入多个成员,是不支持的。但是也可能会成功,但是往往最终都是可能失败或者超时。

  • 第6行 指示插件是否引导组。

    重要

    这个选项任何时候都只能在一个服务器实例上使用, 通常是第一次引导组(或者整个组down了,然后重新启动时)。如果您多次引导这个组,如,当多个服务器实例都设置了这个选项,那么它们就可以创造出一个认为的脑裂场景,其中两个不同的组有一个相同的名。所以在第一台服务器实例启动上线后,禁用掉这个选项。

组中所有的服务器的配置都很类似。需要单独配置的(如: server_iddatadirgroup_replication_local_address),在后面会介绍到。

21.2.1.3 用户认证

组复制使用一步复制协议来实现分布式恢复,在将组成员加入到组之前进行同步。分布式恢复进程依赖于一个名为 group_replication_recovery 的复制通道,改通道用于组成员之间传输事务。因此,需要一个具有正确权限的复制用户,以便于组复制可以在组成员之间建立复制通道。

启动服务器:

mysql-5.7/bin/mysqld --defaults-file=data/s1/s1.cnf

创建一个具有 REPLICATION-SLAVE 权限的MySQL用户。这个过程应该被二进制日志捕获,以避免此更改传递到其他服务器实例上。下面的示例中,显示了创建用户 rpl_user使用密码为 rpl_pass 。在配置服务器时,应使用适当的用户名和密码。连接到服务器 s1,然后执行下面的语句:

mysql> SET SQL_LOG_BIN=0;
Query OK, 0 rows affected (0,00 sec)

mysql> CREATE USER rpl_user@'%' IDENTIFIED BY 'rpl_pass';
Query OK, 0 rows affected (0,00 sec)

mysql> GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%';
Query OK, 0 rows affected, 1 warning (0,00 sec)

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0,00 sec)

mysql> SET SQL_LOG_BIN=1;
Query OK, 0 rows affected (0,00 sec)

创建完用户后,使用 CHANGE MASTER TO 语句,用给定的认证来配置配置服务器后面因为恢复数据而需要用到的 group_replication_recovery复制通道,使用创建的用户和密码替换掉下面语句中的 rpl_userrpl_pass ,然后执行语句。

mysql> CHANGE MASTER TO MASTER_USER='rpl_user', MASTER_PASSWORD='rpl_pass' \\
		      FOR CHANNEL 'group_replication_recovery';
Query OK, 0 rows affected, 2 warnings (0,01 sec)
      

加入组,第一步就是要对加入组的服务器进行分布式恢复。如果上面的认证失败,那么服务器就不能进行恢复过程,就不能和组的其它成员同步,最终也就不能加入该组。同样的,如果成员不能通过服务器的hostname正确地识别其他成员,那么恢复过程可能会失败。 所以通常建议运行MySQL的操作系统配置了一个合适的唯一的hostname,这样,不管使用DNS 或是本地设置。都可以从 performance_schema.replication_group_members表中的Member_host列验证hostname 。如果多个组成员使用的是操作系统的默认的主机名,那么有可能成员不解析正确的成员地址,而导致不能加入组。这种情况下,使用 report_host为每个服务器配置一个唯一的外部 hostname

21.2.1.4 启动组复制

一旦服务器 s1 配置好并启动了,然后就安装组复制插件。连接到服务器,然后执行下面的命令:

INSTALL PLUGIN group_replication SONAME 'group_replication.so';
重要

在加载组复制之前,mysql.session用户必须存在。从 mysql.session 是MySQL 8.0.2添加的。如果您的数据字典是老版本初始化的,那么您必须运行 mysql_upgrade 来升级。如果没有进行升级,那么组复制会启动失败,报出这样的错误信息: There was an error when trying to access the server with user: mysql.session@localhost. Make sure the user is present in the server and that mysql_upgrade was ran after a server update..

检查插件是否安装成功,执行 SHOW PLUGINS;,输出内容应该像这样:

mysql> SHOW PLUGINS;
+----------------------------+----------+--------------------+----------------------+-------------+
| Name                       | Status   | Type               | Library              | License     |
+----------------------------+----------+--------------------+----------------------+-------------+
| binlog                     | ACTIVE   | STORAGE ENGINE     | NULL                 | PROPRIETARY |

(...)

| group_replication          | ACTIVE   | GROUP REPLICATION  | group_replication.so | PROPRIETARY |
+----------------------------+----------+--------------------+----------------------+-------------+

指定服务器s1引导组并启动组复制。引导程序应该只由一个服务器完成,并由这个服务器启动组,而且只进行一次引导。这就是为什么引导配置不会保存在配置文件中。如果它保存在配置文件中,后面重启这个服务器的时候,就会自动的使用相同的名字再次引导组,这样就导致两个不同的组有相同的名。同样的道理,在关闭和重启插件时将这个选项设置为 ON

SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF;

一旦START GROUP_REPLICATION语句结束,组就被启动了。您可以检查刚被创建的组,然后组中有一个成员:

mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+---------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE  |
+---------------------------+--------------------------------------+-------------+-------------+---------------+
| group_replication_applier | ce9be252-2b71-11e6-b8f4-00212844f856 | myhost      |       24801 | ONLINE        |
+---------------------------+--------------------------------------+-------------+-------------+---------------+
1 row in set (0,00 sec)

表中的显示的信息证实了,组中的成员有一个唯一的标识 ce9be252-2b71-11e6-b8f4-00212844f856,并且,它是处于 is ONLINE ,并且名为 myhost的主机上,对客户端的监听端口为 24801

为了演示服务器确实是在一个组中,并且它能够处理负载。现在创建一个表并添加一些内容。

mysql> CREATE DATABASE test;
Query OK, 1 row affected (0,00 sec)

mysql> use test
Database changed
mysql> CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 TEXT NOT NULL);
Query OK, 0 rows affected (0,00 sec)

mysql> INSERT INTO t1 VALUES (1, 'Luis');
Query OK, 1 row affected (0,01 sec)

检查表t1的内容和二进制日志。

mysql> SELECT * FROM t1;
+----+------+
| c1 | c2   |
+----+------+
|  1 | Luis |
+----+------+
1 row in set (0,00 sec)

mysql> SHOW BINLOG EVENTS;
+---------------+-----+----------------+-----------+-------------+--------------------------------------------------------------------+
| Log_name      | Pos | Event_type     | Server_id | End_log_pos | Info                                                               |
+---------------+-----+----------------+-----------+-------------+--------------------------------------------------------------------+
| binlog.000001 |   4 | Format_desc    |         1 |         123 | Server ver: 5.7.17-gr080-log, Binlog ver: 4                        |
| binlog.000001 | 123 | Previous_gtids |         1 |         150 |                                                                    |
| binlog.000001 | 150 | Gtid           |         1 |         211 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:1'  |
| binlog.000001 | 211 | Query          |         1 |         270 | BEGIN                                                              |
| binlog.000001 | 270 | View_change    |         1 |         369 | view_id=14724817264259180:1                                        |
| binlog.000001 | 369 | Query          |         1 |         434 | COMMIT                                                             |
| binlog.000001 | 434 | Gtid           |         1 |         495 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:2'  |
| binlog.000001 | 495 | Query          |         1 |         585 | CREATE DATABASE test                                               |
| binlog.000001 | 585 | Gtid           |         1 |         646 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:3'  |
| binlog.000001 | 646 | Query          |         1 |         770 | use `test`; CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 TEXT NOT NULL) |
| binlog.000001 | 770 | Gtid           |         1 |         831 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:4'  |
| binlog.000001 | 831 | Query          |         1 |         899 | BEGIN                                                              |
| binlog.000001 | 899 | Table_map      |         1 |         942 | table_id: 108 (test.t1)                                            |
| binlog.000001 | 942 | Write_rows     |         1 |         984 | table_id: 108 flags: STMT_END_F                                    |
| binlog.000001 | 984 | Xid            |         1 |        1011 | COMMIT /* xid=38 */                                                |
+---------------+-----+----------------+-----------+-------------+--------------------------------------------------------------------+
15 rows in set (0,00 sec)

正如上所示,库和表被创建了,并且它们相应的DDL语句被写到二进制日志中。同样,数据插入到表中,并写到二进制日志中。 二进制日志条目的重要性在以下章节中得到了说明:当组扩大时,新组员试图赶上并上线,就会执行分布式。

21.2.1.5 组添加实例

此时,组中有一个成员,就是服务器s1。那么现在是通过添加前面配置的其它两个服务器来扩展该组的时候了。

21.2.1.5.1 添加第二个实例

为了添加第二实例,服务器s2,首先为其创建配置文件。配置文件和服务器s1使用的类似。除了数据目录的位置,端口和 server_id。下面的清单中突出的行显示了不同之处。

[mysqld]

# server configuration
datadir=<full_path_to_data>/data/s2
basedir=<full_path_to_bin>/mysql-5.7/

port=24802
socket=<full_path_to_sock_dir>/s2.sock

#
# Replication configuration parameters
#
server_id=2
gtid_mode=ON
enforce_gtid_consistency=ON
master_info_repository=TABLE
relay_log_info_repository=TABLE
binlog_checksum=NONE
log_slave_updates=ON
log_bin=binlog
binlog_format=ROW

#
# Group Replication configuration
#
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_local_address= "127.0.0.1:24902"
loose-group_replication_group_seeds= "127.0.0.1:24901,127.0.0.1:24902,127.0.0.1:24903"
loose-group_replication_bootstrap_group= off

与服务器s1的过程类似,配置完配置文件后启动服务器。

mysql-5.7/bin/mysqld --defaults-file=data/s2/s2.cnf

然后配置恢复认证,由于用户在组中是共享的,所以命令跟当初配置服务器s1一样,在服务器s2上执行下面的语句。

SET SQL_LOG_BIN=0;
CREATE USER rpl_user@'%';
GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%' IDENTIFIED BY 'rpl_pass';
SET SQL_LOG_BIN=1;
CHANGE MASTER TO MASTER_USER='rpl_user', MASTER_PASSWORD='rpl_pass' \\
	FOR CHANNEL 'group_replication_recovery';

安装组复制插件,然后开始将服务器加入到组。下面的示例使用与部署服务器s1时相同的方式安装插件。

mysql> INSTALL PLUGIN group_replication SONAME 'group_replication.so';
Query OK, 0 rows affected (0,01 sec)

添加服务器 s2 到组。

mysql> START GROUP_REPLICATION;
Query OK, 0 rows affected (44,88 sec)

与之前s1的步骤有相同也有不同,这里不同之处就是,在开始启动组复制前, 执行 SET GLOBAL group_replication_bootstrap_group=ON;因为组已经被创建了,并且已经由s1引导过了。那么此时,仅需要将s2添加到已存在的组即可。

小窍门

当组复制启动成功,并且服务器添加到组,如果变量 super_read_only variable is 设置为 1 ,那么变量 super_read_only 设置为 0。 通过在成员的配置文件中设置 super_read_only 为 1 ,就可以确保在启动组复制时,失败的服务器不接受事务。

再次检查 performance_schema.replication_group_members 表中的信息,现在有两个 ONLINE 的服务器在组中。

mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+---------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE  |
+---------------------------+--------------------------------------+-------------+-------------+---------------+
| group_replication_applier | 395409e1-6dfa-11e6-970b-00212844f856 | myhost      |       24801 | ONLINE        |
| group_replication_applier | ac39f1e6-6dfa-11e6-a69d-00212844f856 | myhost      |       24802 | ONLINE        |
+---------------------------+--------------------------------------+-------------+-------------+---------------+
2 rows in set (0,00 sec)

因为s2同样已被标记为ONLINE,所以它一定已经自动的赶上了s1。验证它是否确实与服务器s1同步。

mysql> SHOW DATABASES LIKE 'test';
+-----------------+
| Database (test) |
+-----------------+
| test            |
+-----------------+
1 row in set (0,00 sec)

mysql> SELECT * FROM test.t1;
+----+------+
| c1 | c2   |
+----+------+
|  1 | Luis |
+----+------+
1 row in set (0,00 sec)

mysql> SHOW BINLOG EVENTS;
+---------------+------+----------------+-----------+-------------+--------------------------------------------------------------------+
| Log_name      | Pos  | Event_type     | Server_id | End_log_pos | Info                                                               |
+---------------+------+----------------+-----------+-------------+--------------------------------------------------------------------+
| binlog.000001 |    4 | Format_desc    |         2 |         123 | Server ver: 5.7.17-log, Binlog ver: 4                              |
| binlog.000001 |  123 | Previous_gtids |         2 |         150 |                                                                    |
| binlog.000001 |  150 | Gtid           |         1 |         211 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:1'  |
| binlog.000001 |  211 | Query          |         1 |         270 | BEGIN                                                              |
| binlog.000001 |  270 | View_change    |         1 |         369 | view_id=14724832985483517:1                                        |
| binlog.000001 |  369 | Query          |         1 |         434 | COMMIT                                                             |
| binlog.000001 |  434 | Gtid           |         1 |         495 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:2'  |
| binlog.000001 |  495 | Query          |         1 |         585 | CREATE DATABASE test                                               |
| binlog.000001 |  585 | Gtid           |         1 |         646 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:3'  |
| binlog.000001 |  646 | Query          |         1 |         770 | use `test`; CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 TEXT NOT NULL) |
| binlog.000001 |  770 | Gtid           |         1 |         831 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:4'  |
| binlog.000001 |  831 | Query          |         1 |         890 | BEGIN                                                              |
| binlog.000001 |  890 | Table_map      |         1 |         933 | table_id: 108 (test.t1)                                            |
| binlog.000001 |  933 | Write_rows     |         1 |         975 | table_id: 108 flags: STMT_END_F                                    |
| binlog.000001 |  975 | Xid            |         1 |        1002 | COMMIT /* xid=30 */                                                |
| binlog.000001 | 1002 | Gtid           |         1 |        1063 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:5'  |
| binlog.000001 | 1063 | Query          |         1 |        1122 | BEGIN                                                              |
| binlog.000001 | 1122 | View_change    |         1 |        1261 | view_id=14724832985483517:2                                        |
| binlog.000001 | 1261 | Query          |         1 |        1326 | COMMIT                                                             |
+---------------+------+----------------+-----------+-------------+--------------------------------------------------------------------+
19 rows in set (0,00 sec)

正如之前显示的,第二台服务器已经添加到组,并且自动的复制了来自s1的改变。根据分布式恢复流程,这就意味着,在加入组并在ONLINE声明之前,服务器s2会自动连接到服务器s1,并从s1那里获取缺失的数据。换句话说,当正式加入到组时,它已经将缺失的事务从s1是二进制日志中复制过来了。

21.2.1.5.2 添加更多实例

继续添加实例到组,操作步骤和顺序基本上跟添加第二台服务器一样。除了针对s2的必要配置修改。总结所需的命令:

1. 创建配置文件

[mysqld]

# server configuration
datadir=<full_path_to_data>/data/s3
basedir=<full_path_to_bin>/mysql-5.7/

port=24803
socket=<full_path_to_sock_dir>/s3.sock

#
# Replication configuration parameters
#
server_id=3
gtid_mode=ON
enforce_gtid_consistency=ON
master_info_repository=TABLE
relay_log_info_repository=TABLE
binlog_checksum=NONE
log_slave_updates=ON
log_bin=binlog
binlog_format=ROW

#
# Group Replication configuration
#
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_local_address= "127.0.0.1:24903"
loose-group_replication_group_seeds= "127.0.0.1:24901,127.0.0.1:24902,127.0.0.1:24903"
loose-group_replication_bootstrap_group= off

2. 启动服务器

mysql-5.7/bin/mysqld --defaults-file=data/s3/s3.cnf

3. 为 group_replication_recovery 通道配置恢复认证

SET SQL_LOG_BIN=0;
CREATE USER rpl_user@'%';
GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%' IDENTIFIED BY 'rpl_pass';
FLUSH PRIVILEGES;
SET SQL_LOG_BIN=1;
CHANGE MASTER TO MASTER_USER='rpl_user', MASTER_PASSWORD='rpl_pass'  \\
FOR CHANNEL 'group_replication_recovery';

4. 安装组复制插件并启动它

INSTALL PLUGIN group_replication SONAME 'group_replication.so';
START GROUP_REPLICATION;

至此,服务器s3已启动并运行,且加入到组,然后恢复数据直到跟其他组员一样。再次查询 performance_schema.replication_group_members 表信息,来证实我们所说的。

mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+---------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE  |
+---------------------------+--------------------------------------+-------------+-------------+---------------+
| group_replication_applier | 395409e1-6dfa-11e6-970b-00212844f856 | myhost      |       24801 | ONLINE        |
| group_replication_applier | 7eb217ff-6df3-11e6-966c-00212844f856 | myhost      |       24803 | ONLINE        |
| group_replication_applier | ac39f1e6-6dfa-11e6-a69d-00212844f856 | myhost      |       24802 | ONLINE        |
+---------------------------+--------------------------------------+-------------+-------------+---------------+
3 rows in set (0,00 sec)

在s2或s1上执行相同的查询,结果是一样的。同样,您可以可以验证s3是否恢复完成:

mysql> SHOW DATABASES LIKE 'test';
+-----------------+
| Database (test) |
+-----------------+
| test            |
+-----------------+
1 row in set (0,00 sec)

mysql> SELECT * FROM test.t1;
+----+------+
| c1 | c2   |
+----+------+
|  1 | Luis |
+----+------+
1 row in set (0,00 sec)

mysql>  SHOW BINLOG EVENTS;
+---------------+------+----------------+-----------+-------------+--------------------------------------------------------------------+
| Log_name      | Pos  | Event_type     | Server_id | End_log_pos | Info                                                               |
+---------------+------+----------------+-----------+-------------+--------------------------------------------------------------------+
| binlog.000001 |    4 | Format_desc    |         3 |         123 | Server ver: 5.7.17-log, Binlog ver: 4                              |
| binlog.000001 |  123 | Previous_gtids |         3 |         150 |                                                                    |
| binlog.000001 |  150 | Gtid           |         1 |         211 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:1'  |
| binlog.000001 |  211 | Query          |         1 |         270 | BEGIN                                                              |
| binlog.000001 |  270 | View_change    |         1 |         369 | view_id=14724832985483517:1                                        |
| binlog.000001 |  369 | Query          |         1 |         434 | COMMIT                                                             |
| binlog.000001 |  434 | Gtid           |         1 |         495 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:2'  |
| binlog.000001 |  495 | Query          |         1 |         585 | CREATE DATABASE test                                               |
| binlog.000001 |  585 | Gtid           |         1 |         646 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:3'  |
| binlog.000001 |  646 | Query          |         1 |         770 | use `test`; CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 TEXT NOT NULL) |
| binlog.000001 |  770 | Gtid           |         1 |         831 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:4'  |
| binlog.000001 |  831 | Query          |         1 |         890 | BEGIN                                                              |
| binlog.000001 |  890 | Table_map      |         1 |         933 | table_id: 108 (test.t1)                                            |
| binlog.000001 |  933 | Write_rows     |         1 |         975 | table_id: 108 flags: STMT_END_F                                    |
| binlog.000001 |  975 | Xid            |         1 |        1002 | COMMIT /* xid=29 */                                                |
| binlog.000001 | 1002 | Gtid           |         1 |        1063 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:5'  |
| binlog.000001 | 1063 | Query          |         1 |        1122 | BEGIN                                                              |
| binlog.000001 | 1122 | View_change    |         1 |        1261 | view_id=14724832985483517:2                                        |
| binlog.000001 | 1261 | Query          |         1 |        1326 | COMMIT                                                             |
| binlog.000001 | 1326 | Gtid           |         1 |        1387 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:6'  |
| binlog.000001 | 1387 | Query          |         1 |        1446 | BEGIN                                                              |
| binlog.000001 | 1446 | View_change    |         1 |        1585 | view_id=14724832985483517:3                                        |
| binlog.000001 | 1585 | Query          |         1 |        1650 | COMMIT                                                             |
+---------------+------+----------------+-----------+-------------+--------------------------------------------------------------------+
23 rows in set (0,00 sec)

21.3 监控组复制

使用 Perfomance Schema 表来监控组复制。假设已经启用了 Performance Schema,组复制会添加下列表:

  • performance_schema.replication_group_member_stats

  • performance_schema.replication_group_members

这些Perfomance Schema 复制表还可以显示组复制的信息:

  • performance_schema.replication_connection_status

  • performance_schema.replication_applier_status

复制通道是有组复制插件创建的,名为:

  • group_replication_recovery - 此通道用于与分布式恢复阶段相关的复制更改。

  • group_replication_applier - 这个通道是用于组变化的传入。是用于应用直接来自组的事务的通道。

下面的部分描述了这些表中可用的信息。

21.3.1 Replication_group_member_stats

没有组员都有一个组复制认证,并应用从组那里接受到的事务。对于认证和应用程序的统计数据,有助于理解应用器序列如果增长,建立了多少个认证,检查了多少个事务,那个地方的事务被提交,等等。

The performance_schema.replication_group_member_stats表能够提供与认证过程相关的组级信息。同样,还可以对复制组的每个成员接收和发起的事务数据进行统计。这些信息在组内都是共享的,所以可以在任何组员上查询这些信息,注意的是,对远程组员统计信息的刷新周期是通过 group_replication_flow_control_period选项控制,所以这就跟本地自己的统计信息略微不同。

表 21.1 replication_group_member_stats

字段

描述

Channel_name

组复制的通道名

View_id

当前组的标识

Member_id

成员服务器的 UUID,在组中是唯一的,每个组员都不一样。

Count_transactions_in_queue

The number of transactions in the queue pending conflict detection checks. Once the transactions have been checked for conflicts, if they pass the check, they are queued to be applied as well.

Count_transactions_checked

The number of transactions that have been checked for conflicts.

Count_conflicts_detected

The number of transactions that did not pass the conflict detection check.

Count_transactions_rows_validating

The current size of the conflict detection database (against which each transaction is certified).

Transactions_committed_all_members

The transactions that have been successfully committed on all members of the replication group. This is updated at a fixed time interval.

Last_conflict_free_transaction

The transaction identifier of the last conflict free transaction checked.

Count_transactions_remote_in_applier_queue

The number of transactions that this member has received from the replication group which are waiting to be applied.

Count_transactions_remote_applied

The number of transactions that this member has received from the replication group which have been applied.

Count_transactions_local_proposed

The number of transactions that this member originated and sent to the replication group for coordination.

Count_transactions_local_rollback

The number of transactions that this member originated that were rolled back after being sent to the replication group.


These fields are important for monitoring the performance of the members connected in the group. For example, suppose that one of the group’s members is delayed and is not able to keep up to date with the other members of the group. In this case you might see a large number of transactions in the queue. Based on this information, you could decide to either remove the member from the group, or delay the processing of transactions on the other members of the group in order to reduce the number of queued transactions. This information can also help you to decide how to adjust the flow control of the Group Replication plugin.

21.3.2 Replication_group_members

This table is used for monitoring the status of the different server instances that are tracked in the current view, or in other words are part of the group and as such are tracked by the membership service. The information is shared between all the server instances that are members of the replication group, so information on all the group members can be queried from any member.

Table 21.2 replication_group_members

Field

Description

Channel_name

The name of the Group Replication channel.

Member_id

The member server UUID. This has a different value for each member in the group. This also serves as a key because it is unique to each member.

Member_host

The network address of the group member.

Member_port

The MySQL connection port on which the group member is listening.

Member_state

The current state of the group member (which can be ONLINE, ERROR, RECOVERING, OFFLINE or UNREACHABLE).

Member_role

The role of the member in the group, either PRIMARY or SECONDARY.

Member_version

The MySQL version of the group member.


For more information about the Member_host value and its impact on the distributed recovery process, see 21.2.1.3, “User Credentials”.

21.3.3 Replication_connection_status

When connected to a group, some fields in this table show information regarding Group Replication. For instance, the transactions that have been received from the group and queued in the applier queue (the relay log).

Table 21.3 replication_connection_status

Field

Description

Channel_name

The name of the Group Replication channel.

Group_name

Shows the value of the name of the group. It is always a valid UUID.

Source_UUID

Shows the identifier for the group. It is similar to the group name and it is used as the UUID for all the transactions that are generated during group replication.

Service_state

Shows whether the member is a part of the group or not. The possible values of service state can be {ON, OFF and CONNECTING};

Received_transaction_set

Transactions in this GTID set have been received by this member of the group.


21.3.4 Replication_applier_status

The state of the Group Replication related channels and threads can be observed using the regular replication_applier_status table as well. If there are many different worker threads applying transactions, then the worker tables can also be used to monitor what each worker thread is doing.

Table 21.4 replication_applier_status

Field

Description

Channel_name

The name of the Group Replication channel.

Service_state

Shows whether the applier service is ON or OFF.

Remaining_delay

Shows whether there is some applier delay configured.

Count_transactions_retries

The number of retries performed while applying a transaction.

Received_transaction_set

Transactions in this GTID set have been received by this member of the group.


21.3.5 Group Replication Server States

The table replication_group_members is updated whenever there is a view change, for example when the configuration of the group is dynamically changed. At that point, servers exchange some of their metadata to synchronize themselves and continue to cooperate together.

There are various states that a server instance can be in. If servers are communicating properly, all report the same states for all servers. However, if there is a network partition, or a server leaves the group, then different information may be reported, depending on which server is queried. Note that if the server has left the group then obviously it cannot report updated information about other servers' state. If there is a partition, such that quorum is lost, servers are not able to coordinate between themselves. As a consequence, they cannot guess what the status of different servers is. Therefore, instead of guessing their state they report that some servers are unreachable.

Table 21.5 Server State

范围

描述

组同步

ONLINE

The member is ready to serve as a fully functional group member, meaning that the client can connect and start executing transactions.

Yes

RECOVERING

The member is in the process of becoming an active member of the group and is currently going through the recovery process, receiving state information from a donor.

No

OFFLINE

The plugin is loaded but the member does not belong to any group.

No

ERROR

The state of the member. Whenever there is an error on the recovery phase or while applying changes, the server enters this state.

No

UNREACHABLE

Whenever the local failure detector suspects that a given server is not reachable, because maybe it has crashed or was disconnected involuntarily, it shows that server's state as 'unreachable'.

No


重要

一旦实例进入ERROR 状态,那么 super_read_only 选项就设置为了ON。想要结束ERROR状态,您必须手动的将实例配置为 super_read_only=OFF

注意,组复制不是同步的,但最终是同步的。更准确地说,事务以相同的顺序交付给所有的组成员,但是它们的执行不是同步的,这意味着在一个事务被接受之后,每个成员都以自己的速度提交。

21.4 组复制操作

本节描述了不同模式的部署组复制,解释了管理组的常见操作,并提供了如何调优组的信息。

21.4.1 多主或单主模式部署

组复制有下面几种不同模式:

  • 单主模式

  • 多主模式

默认的模式是单主模式。不能让组的成员部署在不同的模式中,例如在多主模式下配置的一个成员,而另一个成员在单主模式下。要在模式之间切换,组而不是服务器,需要使用不同的操作配置重新启动。要切换租的模式,需要使用不同的操作配置来重启组,而不是服务器。无论部署模式如何,组复制都不能处理客户端故障转移,必须由应用程序本身、连接器或中间件框架来处理,比如代理或路由器。

当在多主模式下部署时,会检查语句以确保它们与模式兼容。多主模式下部署组复制时,将进行以下检查:

  • 如果一个事务是在SERIALIZABLE隔离级别下执行,那么,当它与组同步时,事务提交就会失败。

  • 如果一个事务的执行,违反了表的外键级联约束,那么,当与组同步时,这个事务就无法提交。

可以通过设置选项 group_replication_enforce_update_everywhere_checksFALSE来禁用这些检查。当部署在单主模式,这些选项必须设置为FALSE

21.4.1.1 单主模式

这种模式的组,只有一个主服务器,设置为读写模式,组中其他所有的成员都设置为只读模式(使用 super-read-only=ON ).这些都是自动产生的,主通常是引导组的第一台服务器,其他所有的服务器学习主服务器自动的加入组,并设置为只读模式。

图 21.5 选举新主

New Primary Election

当在单主模式下,有些在多主模式下启用的校验,在单主模式下被禁用了。因为系统强制一个组中,任何时候都只有一个主进行写操作。例如:在单主模式下,可以对有外键级联约束的表进行删除操作,而在多主模式下,这样是不能的。当主这个成员失败时,自动选主机制会的在剩余的成员中选出一个新的成员作为主服务器。新的主服务器选出的方式,是根据服务器的变量server_uuid 和变量 group_replication_member_weight

通过查询一个新的视图,然后跟视图中候选主的 group_replication_member_weight值进行排序, group_replication_member_weight值最大的成员会被选为新的主服务器。如果多个成员服务器有相同的 group_replication_member_weight值,那么就会根据成员的 server_uuid的字典顺序排序,然后选择第一个作为新的主服务器。一旦新的主服务器被选出,它会被自动的设置为读写模式,而剩下的成员服务器都会被自动的设置为只读模式。

最好是在新的主服务器选出之后,再让客户端应用程序连接到数据库。

21.4.1.2 多主模式

在多主模式下,没有单一主次的概念。因为没有成员服务器扮演任何特殊角色,所以不需要进行选主程序。

图21.6 客户端故障转移

Server s1's Clients Must Failover

当加入组时,所有的服务器都被设置为读写模式。

21.4.1.3 发现主

下面的示例展示了如何在单主模式下部署时发现哪个服务器是当前的主服务器。

mysql> SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME= 'group_replication_primary_member';
+--------------------------------------+
| VARIABLE_VALUE                       |
+--------------------------------------+
| 69e1a3b8-8397-11e6-8e67-bf68cbc061a4 |
+--------------------------------------+
1 row in set (0,00 sec)

21.4.2 调优恢复

每当有新组员加入组时,它就会连接到一个合适的捐赠者,并获取它错过的数据,直到它在网上被宣布。组复制中的这个关键组件是容错和可配置的。 组复制中关键的部分就是容错和可配置。下面小节将解释恢复是如何进行的,以及怎么调整设置。

供体的选择

供体是随机选择的,这样,当多个成员进入组时,相同的服务器就不会被选择多次。

如果与选择的供体连接失败,就会自动尝试选择并连接到一个新的供体,一旦达到重试连接的次数的限制,恢复过程最终会以错误结束。

注意

供体是从当前视图中列出的在线组员中随机选择的。

增强自动切换供体

整个恢复的另一个主要问题就是能够确保能恢复过程够应对失败。因此,组复制提供了强大的错误检测机制。在早期版本的组复制中,当与供体连接时,恢复只能检测到因身份验证问题或其它某些问题而导致的连接错误。而现在,对于这种问题场景的反应是切换到一个新的供体,如此一来,就可以在一个不同的组员上进行新的连接尝试。

这种方式同样适用于其它失败场景:

  • 数据被清理掉的场景 - 如果所选供体的一些数据被清除了,而这些数据是恢复过程所需要的,那么就会出现错误。此时恢复会检测出这个错误,并选择一个新的供体。

  • 重复的数据 - 如果加入者含有的一些数据,与在恢复过程中所选的供体的数据相冲突,那么就会出现错误。这可能是在加入者中,出现的一些错误的事务引起的。

    有人可能会说,恢复应该失败,而不是转向另外一个供体。而在不同的组中,有可能有些很有已经共享了这些有冲突的事务,而另外一些组员没有。所以出于这样的原因,和上面的报错,恢复会从组中选择另外一个供体。

  • 其它错误 - 如果恢复线程都失败,(receiver 或 applier 线程失败) 就会显示错误,然后切换到新的供体上恢复。

重试连接供体

恢复数据的传输依赖于二进制日志和现有的MySQL复制框架,因此有可能出现一些短暂的错误,这些错误可能会导致接收或应用线程错误。这种情况下,就有了供体切换的重试功能,类似于在常规复制中发现的功能。

重试的次数

加入者尝试连接供体的次数是10,这个可以通过配置插件变量 group_replication_recovery_retry_count来设置次数。下面的命令是将尝试的最大次数设置为10。

SET GLOBAL group_replication_recovery_retry_count= 10;

注意,这个值是全局的,是加入者尝试与每一个合适的供体连接的次数。

休眠

插件变量 group_replication_recovery_reconnect_interval 是用来定义每隔多长时间对供体进行重新连接。默认值是每隔60秒 ,这个参数可以动态的修改。下面将重连的间隔时间设置为120秒。

SET GLOBAL group_replication_recovery_reconnect_interval= 120;

但是,请注意。因为加入者每次是连接到不同的服务器,而不是一次有一次的对同一台服务器进行连接。它是假定服务器A的问题,可能不会影响到服务器B,因此只有当它对所有可能连接的服务器进行尝试后,恢复过程才会挂起。而此时,会恢复过程会按照变量group_replication_recovery_reconnect_interval配置的时间休眠。

21.4.3 网络分割

这是常规事务的情况,无论什么时候发现复制时,组都必须达成一致,而且还需要进行组员关系的更改及一些内部消息传递,以至于保持组的一致性。 一致,是要求大多数组员在一个给定的决定上达成一致。当大多数群体组员没有达成一致时,那么该组就不能继续和加锁,因为它没有获得多数或规定的票数。

当出现多个组员意外故障时,导致大多数组员从组中删除,这样规定的票数可能就没意义。例如:在一个5个成员的组中,如果其中3个服务器同时无响应,那么大多数服务器就无效,就不能达到规定的票数。事实就是,剩下的2太服务器无法判断其它2个服务器是否已经崩溃了,或者一个网络分区是否已经将这2太服务器孤立了。因此,就无法自动重新配置这个组。

另一方面。如果服务器是自愿退出组,那么就会指示该组重新进行自我配置。是实践中,这就意味着,一个正在离开的服务器,告诉其它组员它正在离开。这样意味着其它组员可以正确的重新配置组,以保持组员的一致性,并且这个“大多数”也会被重新计算。例如:在上面的场景中,5个服务器,有3个要离开,如果这3个服务器在离开的时候告诉组,它们要离开组,一个接一个的,那么组员能够将自己从5调整到2,同时,也保证了规定的票数。

注意

无效的仲裁数本身就是一个不良计划的副作用。所以需要预先设计好失败组员的个数(不管是连续故障还是,一次性全部故障)。

下面的部分解释了如果系统分区是以这样一种方式分区,该组中的服务器不会自动实现仲裁数,那么该怎么做。

21.4.3.1 检测分割

performance schema 中的replication_group_members表,是从服务器的角度,来显示每个服务器在当前视图中的状态。大多数情况下,系统不会运行到分割, 因此,组中所有服务器的这个表,显示的信息是一致的。换句话说,这个表中显示的每个服务器的状态,都是当前视图都认可的。因此,如果现在有个网络 分割,而仲裁数无效,那么那些无法联系的服务器,在这个表中显示的状态是 UNREACHABLE 这些信息由内置在组复制中的本地故障检测器输出的。

图21.7 仲裁数失效

Losing Quorum

To understand this type of network partition the following section describes a scenario where there are initially 5 servers working together correctly, and the changes that then happen to the group once only 2 servers are online. The scenario is depicted in the figure.

As such, lets assume that there is a group with these 5 servers in it:

  • Server s1 with member identifier 199b2df7-4aaf-11e6-bb16-28b2bd168d07

  • Server s2 with member identifier 199bb88e-4aaf-11e6-babe-28b2bd168d07

  • Server s3 with member identifier 1999b9fb-4aaf-11e6-bb54-28b2bd168d07

  • Server s4 with member identifier 19ab72fc-4aaf-11e6-bb51-28b2bd168d07

  • Server s5 with member identifier 19b33846-4aaf-11e6-ba81-28b2bd168d07

Initially the group is running fine and the servers are happily communicating with each other. You can verify this by logging into s1 and looking at its replication_group_members performance schema table. For example:

mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+-------------+-------------+--------------+
| group_replication_applier | 1999b9fb-4aaf-11e6-bb54-28b2bd168d07 | 127.0.0.1   |       13002 | ONLINE       |
| group_replication_applier | 199b2df7-4aaf-11e6-bb16-28b2bd168d07 | 127.0.0.1   |       13001 | ONLINE       |
| group_replication_applier | 199bb88e-4aaf-11e6-babe-28b2bd168d07 | 127.0.0.1   |       13000 | ONLINE       |
| group_replication_applier | 19ab72fc-4aaf-11e6-bb51-28b2bd168d07 | 127.0.0.1   |       13003 | ONLINE       |
| group_replication_applier | 19b33846-4aaf-11e6-ba81-28b2bd168d07 | 127.0.0.1   |       13004 | ONLINE       |
+---------------------------+--------------------------------------+-------------+-------------+--------------+
5 rows in set (0,00 sec)

However, moments later there is a catastrophic failure and servers s3, s4 and s5 stop unexpectedly. A few seconds after this, looking again at the replication_group_members table on s1 shows that it is still online, but several others members are not. In fact, as seen below they are marked as UNREACHABLE. Moreover, the system could not reconfigure itself to change the membership, because the majority has been lost.

mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+-------------+-------------+--------------+
| group_replication_applier | 1999b9fb-4aaf-11e6-bb54-28b2bd168d07 | 127.0.0.1   |       13002 | UNREACHABLE  |
| group_replication_applier | 199b2df7-4aaf-11e6-bb16-28b2bd168d07 | 127.0.0.1   |       13001 | ONLINE       |
| group_replication_applier | 199bb88e-4aaf-11e6-babe-28b2bd168d07 | 127.0.0.1   |       13000 | ONLINE       |
| group_replication_applier | 19ab72fc-4aaf-11e6-bb51-28b2bd168d07 | 127.0.0.1   |       13003 | UNREACHABLE  |
| group_replication_applier | 19b33846-4aaf-11e6-ba81-28b2bd168d07 | 127.0.0.1   |       13004 | UNREACHABLE  |
+---------------------------+--------------------------------------+-------------+-------------+--------------+
5 rows in set (0,00 sec)

The table shows that s1 is now in a group that has no means of progressing without external intervention, because a majority of the servers are unreachable. In this particular case, the group membership list needs to be reset to allow the system to proceed, which is explained in this section. Alternatively, you could also choose to stop Group Replication on s1 and s2 (or stop completely s1 and s2), figure out what happened with s3, s4 and s5 and then restart Group Replication (or the servers).

21.4.3.2 Unblocking a Partition

Group replication enables you to reset the group membership list by forcing a specific configuration. For instance in the case above, where s1 and s2 are the only servers online, you could chose to force a membership configuration consisting of only s1 and s2. This requires checking some information about s1 and s2 and then using the group_replication_force_members variable.

Figure 21.8 Forcing a New Membership

Forcing a New Membership

Suppose that you are back in the situation where s1 and s2 are the only servers left in the group. Servers s3, s4 and s5 have left the group unexpectedly. To make servers s1 and s2 continue, you want to force a membership configuration that contains only s1 and s2.

Warning

This procedure uses group_replication_force_members and should be considered a last resort remedy. It must be used with extreme care and only for overriding loss of quorum. If misused, it could create an artificial split-brain scenario or block the entire system altogether.

Recall that the system is blocked and the current configuration is the following (as perceived by the local failure detector on s1):

mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+-------------+-------------+--------------+
| group_replication_applier | 1999b9fb-4aaf-11e6-bb54-28b2bd168d07 | 127.0.0.1   |       13002 | UNREACHABLE  |
| group_replication_applier | 199b2df7-4aaf-11e6-bb16-28b2bd168d07 | 127.0.0.1   |       13001 | ONLINE       |
| group_replication_applier | 199bb88e-4aaf-11e6-babe-28b2bd168d07 | 127.0.0.1   |       13000 | ONLINE       |
| group_replication_applier | 19ab72fc-4aaf-11e6-bb51-28b2bd168d07 | 127.0.0.1   |       13003 | UNREACHABLE  |
| group_replication_applier | 19b33846-4aaf-11e6-ba81-28b2bd168d07 | 127.0.0.1   |       13004 | UNREACHABLE  |
+---------------------------+--------------------------------------+-------------+-------------+--------------+
5 rows in set (0,00 sec)

The first thing to do is to check what is the peer address (group communication identifier) for s1 and s2. Log in to s1 and s2 and get that information as follows.

mysql> SELECT @@group_replication_local_address;
+-----------------------------------+
| @@group_replication_local_address |
+-----------------------------------+
| 127.0.0.1:10000                   |
+-----------------------------------+
1 row in set (0,00 sec)

Then log in to s2 and do the same thing:

mysql> SELECT @@group_replication_local_address;
+-----------------------------------+
| @@group_replication_local_address |
+-----------------------------------+
| 127.0.0.1:10001                   |
+-----------------------------------+
1 row in set (0,00 sec)

Once you know the group communication addresses of s1 (127.0.0.1:10000) and s2 (127.0.0.1:10001), you can use that on one of the two servers to inject a new membership configuration, thus overriding the existing one that has lost quorum. To do that on s1:

mysql> SET GLOBAL group_replication_force_members="127.0.0.1:10000,127.0.0.1:10001";
Query OK, 0 rows affected (7,13 sec)

This unblocks the group by forcing a different configuration. Check replication_group_members on both s1 and s2 to verify the group membership after this change. First on s1.

mysql> select * from performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+-------------+-------------+--------------+
| group_replication_applier | b5ffe505-4ab6-11e6-b04b-28b2bd168d07 | 127.0.0.1   |       13000 | ONLINE       |
| group_replication_applier | b60907e7-4ab6-11e6-afb7-28b2bd168d07 | 127.0.0.1   |       13001 | ONLINE       |
+---------------------------+--------------------------------------+-------------+-------------+--------------+
2 rows in set (0,00 sec)

And then on s2.

mysql> select * from performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+-------------+-------------+--------------+
| group_replication_applier | b5ffe505-4ab6-11e6-b04b-28b2bd168d07 | 127.0.0.1   |       13000 | ONLINE       |
| group_replication_applier | b60907e7-4ab6-11e6-afb7-28b2bd168d07 | 127.0.0.1   |       13001 | ONLINE       |
+---------------------------+--------------------------------------+-------------+-------------+--------------+
2 rows in set (0,00 sec)

When forcing a new membership configuration, make sure that whatever servers are going to be forced out of the group are indeed stopped. In the scenario depicted above, if s3, s4 and s5 are not really unreachable but instead are online, they may have formed their own functional partition (they are 3 out of 5, hence they have the majority). In that case, forcing a group membership list with s1 and s2 could create an artificial split-brain situation. Therefore it is important before forcing a new membership configuration to ensure that the servers to be excluded are indeed shutdown and if they are not, shut them down before proceding.

21.5 组复制安全性

本节解释如何保护一个组,保护一个组的成员之间的连接,或者使用地址白名单建立一个安全的范围。

21.5.1 IP地址白名单

组复制插件有个一配置选项来决定哪些主机对组的连接可以被接受。这个选项就是 group_replication_ip_whitelist。 如果你在服务器s1上设置了这个选项,然后,当服务器s2建立一个连接到s1,以便参与组通信。那么s1在接受s2的连接前,首先会检查白名单,如果s2在白名单中,s1则接受连接,否咋s1就拒绝s2的连接。

注意

默认情况下,如果没有明确的指定,白名单被自动设置为服务器网络接口的私有网络地址上。

如果您没有配置白名单,服务器会自动的将白名单设置在私网,这就意味着,一个服务器,即使它有公网接口的IP,那么在默认的情况下,还是不能从外部连接。

只要将IP白名单设置为自动,那么在错误日志中就会出现这些的条目,类似于:

2016-07-07T06:40:49.320686Z 4 [Note] Plugin group_replication reported: 'Added automatically \\
IP ranges 10.120.40.237/18,10.178.59.44/22,127.0.0.1/8 to the whitelist'

通过手动设置组允许连接的的IP地列表,就可以进一步提高组的安全性。 这个列表可以用CIDR符号或简单的IP地址来指定。条目之间必须用逗号分开。 例如:

mysql> STOP GROUP_REPLICATION;
mysql> SET GLOBAL group_replication_ip_whitelist="10.120.40.237/18,10.178.59.44/22,127.0.0.1/8";
mysql> START GROUP_REPLICATION;
注意

通常会把 localhost 的IP 地址(127.0.0.1)添加到白名单,而且,没有明确指定,它都是被隐式的自动的添加进去。

21.5.2 安全套接支持(SSL)

MySQL 组复制支持OpenSSL 和 YaSSL。

组间的通信连接以及恢复连接都是使用SSL保护的。下面小节介绍如何配置连接。

为恢复配置

一旦选择了供体,加入者就自动的建立了一个常规的异步复制连接来执行恢复。

但是,加入者使用的用户,需要一个SSL连接,而且必须在加入者连接到供体前创建好。通常情况下,这些操作在服务器加入组之前都已经做好了。

donor> SET SQL_LOG_BIN=0;
donor> CREATE USER 'rec_ssl_user'@'%' REQUIRE SSL;
donor> GRANT replication slave ON *.* TO 'rec_ssl_user'@'%';
donor> SET SQL_LOG_BIN=1;

假设该组中的所有服务器都有一个用于使用SSL的复制用户,那么在连接到供体时,您可以配置加入者,以便可以使用这些凭证。而这是可以根据为组复制插件提供的SSL选项的值完成的。

new_member> SET GLOBAL group_replication_recovery_use_ssl=1;
new_member> SET GLOBAL group_replication_recovery_ssl_ca= '.../cacert.pem';
new_member> SET GLOBAL group_replication_recovery_ssl_cert= '.../client-cert.pem';
new_member> SET GLOBAL group_replication_recovery_ssl_key= '.../client-key.pem';

通过配置恢复通道,来使用需要SSL连接的用户的凭证。

new_member> CHANGE MASTER TO MASTER_USER="rec_ssl_user" FOR CHANNEL "group_replication_recovery";
new_member> START GROUP_REPLICATION;

为组通信配置 SSL

安全套接可用于在组中建立成员之间的通信。而其相关的配置是取决于服务器的SSL配置。因此,如果服务器配置了SSL,那么组复制插件也就配置了SSL。有关配置服务器SSL的选项的更多信息,请参阅 6.4.5, “安全连接的命令选项”。 用于配置组复制的选项如下表。

表 21.6 SSL 选项

服务器配置

插件配置描述

ssl_key

密钥文件路径,客户端和服务器端认证时使用。

ssl_cert

证书路径。客户端和服务器端认证时使用。

ssl_ca

用于互信的SSL CAs文件路径。

ssl_capath

含有用于互信的SSL CAs证书的目录路径。

ssl_crl

含有证书撤销列表的文件的路径。

ssl_crlpath

包含证书回收列表的文件的目录路径。

ssl_cipher

通过连接加密数据时允许使用密码。

tls_version

安全通信将使用此版本及其协议。


这些选项是MySQL服务器配置选项,组复制依赖于它的配置。此外,还有下面的组复制特定选项,可以在插件本身上配置SSL。

表21.7 配置group_replication_ssl_mode值

描述

DISABLED

建立未加密连接(默认)。

REQUIRED

如果服务器支持安全连接,则建立安全连接。

VERIFY_CA

与 REQUIRED 一样,但还要根据已配置的(CA)认证,来验证服务器的TLS证书。

VERIFY_IDENTITY

与VERIFY_CA一样,但是还需要验证服务器证书,是否与所尝试连接的主机匹配。


下面的示例展示了一个示例my.cnf文件段,用于在服务器上配置SSL,以及如何为组复制激活它。

[mysqld]
ssl_ca = "cacert.pem"
ssl_capath = "/.../ca_directory"
ssl_cert = "server-cert.pem"
ssl_cipher = "DHE-RSA-AEs256-SHA"
ssl_crl = "crl-server-revoked.crl"
ssl_crlpath = "/.../crl_directory"
ssl_key = "server-key.pem"
group_replication_ssl_mode= REQUIRED

插件中唯一需要特别配置的选项,就是列出中的 group_replication_ssl_mode。这个选项通过将 服务器提供的 ssl_*参数来配置SSL框架。以激活组成员之间的SSL通信。

21.5.3 虚拟私有网络(VPN)

:在虚拟私网,没有什么可以阻止组复制操作。其核心就是它仅依赖于IPv4套接字来建立服务器之间的连接,以便在它们之间传播消息。

21.6 组复制系统变量

这些系统变量都是特定于组复制插件的变量。每个配置选项都是以 "group_replication"为前缀。

重要

尽管描述中的大部分变量是动态的,可以在服务器运行时改变,但是大部分的改变只有在重启组复制插件后才会生效。本节会特别提到那些不用重启插件就可以生效的变量。

  • group_replication_group_name

    命令行格式--group-replication-group-name=value
    系统变量名字group_replication_group_name
    变量作用域全局
    动态变量
    允许值类型字符串

    服务器实例所属组的名字,必须是一个有效的UUID。

  • group_replication_start_on_boot

    命令行格式--group-replication-start-on-boot=value
    系统变量名字group_replication_start_on_boot
    变量作用域全局
    动态变量
    允许值类型布尔型
    默认值ON

    启动服务器时,指定服务器是否启动组复制。

  • group_replication_local_address

    命令行格式--group-replication-local-address=value
    系统变量名字group_replication_local_address
    变量作用域全局
    动态变量
    允许值类型字符串

    地址格式是 host:port 的字符串。

  • group_replication_member_weight

    Introduced8.0.2
    命令行格式--group-replication-member-weight=value
    系统变量名字group_replication_member_weight
    变量作用域全局
    动态变量
    允许值类型整数
    默认值50
    最小值0
    最大值100

    A percentage weight that can be assigned to members to influence the chance of the member being elected as primary in the event that the existing primary leaves the group. Assign numeric weights to members to ensure that specific members are elected, for example during scheduled maintenance of the primary or to ensure certain hardware is prioritised in the event of failover.

  • group_replication_group_seeds

    命令行格式--group-replication-group-seeds=value
    系统变量名字group_replication_group_seeds
    变量作用域全局
    动态变量
    允许值类型字符串

    A list of peer addresses as a comma separated list such as host1:port1,host2:port2. Each address is validated when starting Group Replication and the list must contain at least one valid address. Any invalid host names could cause START GROUP_REPLICATION to fail.

  • group_replication_force_members

    命令行格式--group-replication-force-members=value
    系统变量名字group_replication_force_members
    变量作用域全局
    动态变量
    允许值类型字符串

    A list of peer addresses as a comma separated list such as host1:port1,host2:port2. This option is used to force a new group membership, in which the excluded members do not receive a new view and are blocked. You need to manually kill the excluded servers. Any invalid host names in the list could cause subsequent START GROUP_REPLICATION statements to fail because they could block group membership.

  • group_replication_bootstrap_group

    命令行格式--group-replication-bootstrap-group=value
    系统变量名字group_replication_bootstrap_group
    变量作用域全局
    动态变量
    允许值类型布尔型
    默认值OFF

    Configure this server to bootstrap the group. This option must only be set on one server and only when starting the group for the first time or restarting the entire group. After the group has been bootstrapped, set this option to OFF. It should be set to OFF both dynamically and in the configuration files. Starting two servers or restarting one server with this option set while the group is running may lead to an artificial split brain situation, where two independent groups with the same name are bootstrapped.

  • group_replication_poll_spin_loops

    命令行格式--group-replication-poll-spin-loops=value
    系统变量名字group_replication_poll_spin_loops
    变量作用域全局
    动态变量
    允许值 (32-bit platforms)类型整数
    默认值0
    最小值0
    最大值4294967295
    允许值 (64-bit platforms)类型整数
    默认值0
    最小值0
    最大值18446744073709547520

    The number of times the group communication thread waits for the communication engine mutex to be released before the thread waits for more incoming network messages.

  • group_replication_recovery_retry_count

    命令行格式--group-replication-recovery-retry-count=value
    系统变量名字group_replication_recovery_retry_count
    变量作用域全局
    动态变量
    允许值类型整数
    默认值10
    最小值0
    最大值31536000

    The number of times that the member that is joining tries to connect to the available donors before giving up.

  • group_replication_recovery_reconnect_interval

    命令行格式--group-replication-recovery-reconnect-interval=value
    系统变量名字group_replication_recovery_reconnect_interval
    变量作用域全局
    动态变量
    允许值类型整数
    默认值60
    最小值0
    最大值0

    The sleep time, in seconds, between reconnection attempts when no donor was found in the group.

  • group_replication_recovery_use_ssl

    命令行格式--group-replication-recovery-use-ssl=value
    系统变量名字group_replication_recovery_use_ssl
    变量作用域全局
    动态变量
    允许值类型布尔型
    默认值OFF

    Whether Group Replication recovery connection should use SSL or not.

  • group_replication_recovery_ssl_ca

    命令行格式--group-replication-recovery-ssl-ca=value
    系统变量名字group_replication_recovery_ssl_ca
    变量作用域全局
    动态变量
    允许值类型字符串

    The path to a file that contains a list of trusted SSL certificate authorities.

  • group_replication_recovery_ssl_capath

    命令行格式--group-replication-recovery-ssl-capath=value
    系统变量名字group_replication_recovery_ssl_capath
    变量作用域全局
    动态变量
    允许值类型字符串

    The path to a directory that contains trusted SSL certificate authority certificates.

  • group_replication_recovery_ssl_cert

    命令行格式--group-replication-recovery-ssl-cert=value
    系统变量名字group_replication_recovery_ssl_cert
    变量作用域全局
    动态变量
    允许值类型字符串

    The name of the SSL certificate file to use for establishing a secure connection.

  • group_replication_recovery_ssl_key

    命令行格式--group-replication-recovery-ssl-key=value
    系统变量名字group_replication_recovery_ssl_key
    变量作用域全局
    动态变量
    允许值类型字符串

    The name of the SSL key file to use for establishing a secure connection.

  • group_replication_recovery_ssl_cipher

    命令行格式--group-replication-recovery-ssl-cipher=value
    系统变量名字group_replication_recovery_ssl_cipher
    变量作用域全局
    动态变量
    允许值类型字符串

    A list of permissible ciphers to use for SSL encryption.

  • group_replication_recovery_ssl_crl

    命令行格式--group-replication-recovery-ssl-crl=value
    系统变量名字group_replication_recovery_ssl_crl
    变量作用域全局
    动态变量
    允许值类型字符串

    The path to a directory that contains files containing certificate revocation lists.

  • group_replication_recovery_ssl_crlpath

    命令行格式--group-replication-recovery-ssl-crlpath=value
    系统变量名字group_replication_recovery_ssl_crlpath
    变量作用域全局
    动态变量
    允许值类型字符串

    The path to a directory that contains files containing certificate revocation lists.

  • group_replication_recovery_ssl_verify_server_cert

    命令行格式--group-replication-recovery-ssl-verify-server-cert=value
    系统变量名字group_replication_recovery_ssl_verify_server_cert
    变量作用域全局
    动态变量
    允许值类型布尔型
    默认值OFF

    Make the recovery process check the server's Common Name value in the donor sent certificate.

  • group_replication_recovery_complete_at

    命令行格式--group-replication-recovery-complete-at=value
    系统变量名字group_replication_recovery_complete_at
    变量作用域全局
    动态变量
    允许值类型enumeration
    默认值TRANSACTIONS_APPLIED
    有效值TRANSACTIONS_CERTIFIED
    TRANSACTIONS_APPLIED

    Recovery policies when handling cached transactions after state transfer. This option specifies whether a member is marked online after it has received all transactions that it missed before it joined the group (TRANSACTIONS_CERTIFIED) or after it has received and applied them (TRANSACTIONS_APPLIED).

  • group_replication_components_stop_timeout

    命令行格式--group-replication-components-stop-timeout=value
    系统变量名字group_replication_components_stop_timeout
    变量作用域全局
    动态变量
    允许值类型整数
    默认值31536000

    Timeout, in seconds, that Group Replication waits for each of the components when shutting down.

  • group_replication_allow_local_lower_version_join

    命令行格式--group-replication-allow-local-lower-version-join=value
    系统变量名字group_replication_allow_local_lower_version_join
    变量作用域全局
    动态变量
    允许值类型布尔型
    默认值OFF

    Allow the current server to join the group even if it has a lower plugin version than the group.

  • group_replication_allow_local_disjoint_gtids_join

    命令行格式--group-replication-allow-local-disjoint-gtids-join=value
    系统变量名字group_replication_allow_local_disjoint_gtids_join
    变量作用域全局
    动态变量
    允许值类型布尔型
    默认值OFF

    Allow the current server to join the group even if it has transactions not present in the group.

    Warning

    Use caution when enabling this option as incorrect usage could lead to inconsistencies in the group.

  • group_replication_auto_increment_increment

    命令行格式--group-replication-auto-increment-increment=value
    系统变量名字group_replication_auto_increment_increment
    变量作用域全局
    动态变量
    允许值类型整数
    默认值7
    最小值1
    最大值65535

    Determines the interval between successive column values for transactions that execute on this server instance.

  • group_replication_compression_threshold

    命令行格式--group-replication-compression-threshold=value
    系统变量名字group_replication_compression_threshold
    变量作用域全局
    动态变量
    允许值类型整数
    默认值1000000
    最小值0
    最大值4294967296

    The value in bytes above which (LZ4) compression is enforced. When set to zero, deactivates compression.

  • group_replication_gtid_assignment_block_size

    命令行格式--group-replication-gtid-assignment-block-size=value
    系统变量名字group_replication_gtid_assignment_block_size
    变量作用域全局
    动态变量
    允许值 (32-bit platforms)类型整数
    默认值1000000
    最小值1
    最大值4294967295
    允许值 (64-bit platforms)类型整数
    默认值1000000
    最小值1
    最大值18446744073709547520

    The number of consecutive GTIDs that are reserved for each member. Each member consumes its blocks and reserves more when needed.

  • group_replication_ssl_mode

    命令行格式--group-replication-ssl-mode=value
    系统变量名字group_replication_ssl_mode
    变量作用域全局
    动态变量
    允许值类型enumeration
    默认值DISABLED
    有效值DISABLED
    REQUIRED
    VERIFY_CA
    VERIFY_IDENTITY

    Specifies the security state of the connection between Group Replication members.

  • group_replication_single_primary_mode

    命令行格式--group-replication-single-primary-mode=value
    系统变量名字group_replication_single_primary_mode
    变量作用域全局
    动态变量
    允许值类型布尔型
    默认值ON

    Instructs the group to automatically pick a single server to be the one that handles read/write workload. This server is the PRIMARY and all others are SECONDARIES.

  • group_replication_transaction_size_limit

    命令行格式--group-replication-transaction-size-limit=value
    系统变量名字group_replication_transaction_size_limit
    变量作用域全局
    动态变量
    允许值类型整数
    默认值0
    最小值0
    最大值2147483647
    允许值 (>= 8.0.2)类型整数
    默认值150000000
    最小值0
    最大值2147483647

    Configures the maximum transaction size in bytes which the group accepts. Transactions larger than this size are rolled back. Use this option to avoid large transactions causing the group to fail. A large transaction can cause problems for a group, either in terms of memory allocation or network bandwidth consumption, which may cause the failure detector to trigger because a given member is unreachable while it is busy processing the large transaction. When set to 0 there is no limit to the size of transactions the group accepts, and there may be the risk of large transactions causing the group to fail. Adjust the value of this variable depending on the size of workload you require from the group.

  • group_replication_unreachable_majority_timeout

    Introduced8.0.2
    命令行格式--group-replication-unreachable-majority-timeout=value
    系统变量名字group_replication_unreachable_majority_timeout
    变量作用域全局
    动态变量
    允许值类型整数
    默认值0
    最小值0
    最大值31536000

    Configures how long members that suffer a network partition and cannot connect to the majority wait before leaving the group. By default set to 0, which means that members that find themselves in a minority due to a network partition wait forever to connect the group. In a group of 5 servers (S1,S2,S3,S4,S5), if there is a disconnection between (S1,S2) and (S3,S4,S5) there is a network partition. The first group (S1,S2) is now in a minority because it cannot contact more than half of the group. While the majority group (S3,S4,S5) remains running, the minority group waits forever for a network reconnection. Any transactions processed by the minority group are blocked until Group Replication is stopped using STOP GROUP REPLICATION on the members of the minority.

    If configured to a number of seconds, members wait for this amount of time after losing contact with the majority of members before leaving the group. All pending transactions processed by the minority are rolled back and the servers in the minority partition move to the ERROR state and set themselves to super_read_only=ON mode.

    Warning

    When you have a symmetric group, with just two members for example (S0,S2), if there is a network partition and there is no majority, after the configured timeout all members shut down and enter ERROR state.

  • group_replication_enforce_update_everywhere_checks

    命令行格式--group-replication-enforce-update-everywhere-checks=value
    系统变量名字group_replication_enforce_update_everywhere_checks
    变量作用域全局
    动态变量
    允许值类型布尔型
    默认值OFF

    Enable or disable strict consistency checks for multi-primary update everywhere.

  • group_replication_flow_control_mode

    命令行格式--group-replication-flow-control-mode=value
    系统变量名字group_replication_flow_control_mode
    变量作用域全局
    动态变量
    允许值类型enumeration
    默认值QUOTA
    有效值DISABLED
    QUOTA

    Specifies the mode used for flow control. This variable can be changed without resetting Group Replication.

  • group_replication_flow_control_hold_percent

    Introduced8.0.2
    命令行格式--group-replication-flow-control-hold-percent=value
    系统变量名字group_replication_flow_control_hold_percent
    变量作用域全局
    动态变量
    允许值类型整数
    默认值10
    最小值0
    最大值100

    Defines what percentage of the group quota remains unused to allow a cluster under flow control to catch up on backlog. A value of 0 implies that no part of the quota is reserved for catching up on the work backlog.

  • group_replication_flow_control_certifier_threshold

    命令行格式--group-replication-flow-control-certifier-threshold=value
    系统变量名字group_replication_flow_control_certifier_threshold
    变量作用域全局
    动态变量
    允许值类型整数
    默认值25000
    最小值0
    最大值2147483648

    Specifies the number of waiting transactions in the certifier queue that trigger flow control. This variable can be changed without resetting Group Replication.

  • group_replication_flow_control_applier_threshold

    命令行格式--group-replication-flow-control-applier-threshold=value
    系统变量名字group_replication_flow_control_applier_threshold
    变量作用域全局
    动态变量
    允许值类型整数
    默认值25000
    最小值0
    最大值2147483648

    Specifies the number of waiting transactions in the applier queue that trigger flow control. This variable can be changed without resetting Group Replication.

  • group_replication_flow_control_max_commit_quota

    Introduced8.0.2
    命令行格式--group-replication-flow-control-max-commit-quota=value
    系统变量名字group_replication_flow_control_max_commit_quota
    变量作用域全局
    动态变量
    允许值类型整数
    默认值0
    最小值0
    最大值2147483648

    Defines the maximum flow control quota of the group, or the maximum available quota for any period while flow control is enabled. A value of 0 implies that there is no maximum quota set. Cannot be smaller than group_replication_flow_control_min_quota and group_replication_flow_control_min_recovery_quota.

  • group_replication_flow_control_member_quota_percent

    Introduced8.0.2
    命令行格式--group-replication-flow-control-member-quota-percent=value
    系统变量名字group_replication_flow_control_member_quota_percent
    变量作用域全局
    动态变量
    允许值类型整数
    默认值0
    最小值0
    最大值100

    Defines the percentage of the quota that a member should assume is available for itself when calculating the quotas. A value of 0 implies that the quota should be split equally between members that were writers in the last period.

  • group_replication_flow_control_min_quota

    Introduced8.0.2
    命令行格式--group-replication-flow-control-min-quota=value
    系统变量名字group_replication_flow_control_min_quota
    变量作用域全局
    动态变量
    允许值类型整数
    默认值0
    最小值0
    最大值2147483648

    Controls the lowest flow control quota that can be assigned to a member, independently of the calculated minimum quota executed in the last period. A value of 0 implies that there is no minimum quota. Cannot be larger than group_replication_flow_control_max_commit_quota.

  • group_replication_flow_control_min_recovery_quota

    Introduced8.0.2
    命令行格式--group-replication-flow-control-min-recovery-quota=value
    系统变量名字group_replication_flow_control_min_recovery_quota
    变量作用域全局
    动态变量
    允许值类型整数
    默认值0
    最小值0
    最大值2147483648

    Controls the lowest quota that can be assigned to a member because of another recovering member in the group, independently of the calculated minimum quota executed in the last period. A value of 0 implies that there is no minimum quota. Cannot be larger than group_replication_flow_control_max_commit_quota.

  • group_replication_flow_control_period

    Introduced8.0.2
    命令行格式--group-replication-flow-control-period=value
    系统变量名字group_replication_flow_control_period
    变量作用域全局
    动态变量
    允许值类型整数
    默认值1
    最小值1
    最大值60

    Defines how many seconds to wait between flow control iterations, in which flow control messages are sent and flow control management tasks are run.

  • group_replication_flow_control_release_percent

    Introduced8.0.2
    命令行格式--group-replication-flow-control-release-percent=value
    系统变量名字group_replication_flow_control_release_percent
    变量作用域全局
    动态变量
    允许值类型整数
    默认值50
    最小值0
    最大值1000

    Defines how the group quota should be released when flow control no longer needs to throttle the writer members, with this percentage being the quota increase per flow control period. A value of 0 implies that once the flow control thresholds are within limits the quota is released in a single flow control iteration. The range allows the quota to be released at up to 10 times current quota, as that allows a greater degree of adaptation, mainly when the flow control period is large and the quotas are very small.

  • group_replication_ip_whitelist

    命令行格式--group-replication-ip-whitelist=value
    系统变量名字group_replication_recovery_ip_whitelist
    变量作用域全局
    动态变量
    允许值类型字符串
    默认值AUTOMATIC

    指定允许哪些主机可以连接到组,默认设置为AUTOMATIC, 这样就允许在主机的私有子网连接。扫描主机上激活的接口,并且自动将其在私有子网上的地址,添加到允许的列表中。或者,您可以使用逗号分隔的IPv4地址或子网CIDR表示法来指定允许连接的主机。如: 192.168.1.0/24,10.0.0.1

    注意

    地址127.0.0.1一直都是允许连接,即使没有使用group_replication_ip_whitelist指定。

21.7 要求和局限性

本节列举和解释组复制的要求和局限性。

21.7.1 组复制要求

想要加入组的服务器实例必须满足下面的要求。

基础结构

  • InnoDB存储引擎  数据必须保存在 InnoDB事务性存储引擎。事务被乐地执行,然后在提交时检查冲突。如果存在冲突,为了在整个组中保持一致性,一些事务将被回滚。这意味着需要一个事务存储引擎。此外,InnoDB提供了一些额外的功能,可以在与组复制一起操作时更好地管理和处理冲突。

  • 主键 每个要被组复制的表都必须有一个明确的主键定义。通过主键确定每个事务修改了哪些行,来确定哪些事务发生冲突,所以主键扮演着极其重要的角色。

  • IPv4 网络  MySQL组复制使用的组通信引擎只支持IPv4。因此,组复制需要一个IPv4的网络基础设施。

  • 网络性能 组复制被设计成部署在一个集群环境中,服务器实例彼此之间非常近,且受到网络延迟和网络带宽的影响。

服务器实例配置

下面的选项必须在组的成员服务器实例上进行配置。

  • 开启二进制日志。  设置 --log-bin[=log_file_name]。MySQL组复制是通过复制二进制日志内容,因此必须开启二进制日志以便于进行复制。参阅 5.4.4, “二进制日志”

  • Slave 更新日志。 设置 --log-slave-updates。 服务器需要记录通过复制applier应用的二进制日志。组中的服务器需要记录所有从组那里接收和应用的事务。而且这是必需的,因为恢复是通过在组中的参与者的二进制日志来进行的。因此,每个事务的副本都需要在每个服务器上存在,即便是那些本身没有启用事务的服务器。

  • 二进制日志格式 。 设置 --binlog-format=row。 组复制依赖于基于行的复制格式,以至于变更在组中的服务器之间是一致性的传输。由于组复制依赖基于行的架构,这样便能够提取必要的信息,以便于 检测在组中不同服务器中并发执行的事务之间的冲突。参阅 18.2.1, “复制格式”

  • 开启全局事务标示符。 设置 --gtid-mode=ON。组复制是使用全局事务标示符来精确的跟踪每个服务器实例上已经提交了哪些事务。 这样就能够推断出那个服务器已执行的事务,可能会与其他地方已执行的事务发生冲突。换句话说就是,显式事务标识符是整个框架的一个基本部分,它可以确定哪些事务可能发生冲突。 参阅, 18.1.3 节, “具有全局事务标识符的复制”

  • 复制的信息存储。 设置--master-info-repository=TABLE --relay-log-info-repository=TABLE。 复制的applier需要将主机的信息和中继日志元数据写到系统表 mysql.slave_master_infomysql.slave_relay_log_info中。这样确保组复制插件对复制元数据具有一致的可恢复性和事务性管理。 18.2.4.2 节, “Slave日志状态”

  • 提取事务写集。 设置--transaction-write-set-extraction=XXHASH64 以至于当收集行,以将它们记录到二进制日志时,服务器也会收集写集。写集合是基于每一行的主键,并且是一个简单且紧凑的标签,它惟一地标识被修改的行。然后使用这个标记来检测冲突。

  • 多线程Appliers。 组复制的成员可以配置为多线程 appliers,使得可以并行的应用事务。设置 --slave-parallel-workers=N (这里的 N 表示并行applier线程的数量)、 --slave-preserve-commit-order=1, 和 --slave-parallel-type=LOGICAL_CLOCK。 在成员服务器上设置 --slave-parallel-workers=N 以启动多线程applier。组复制依赖于建立在保证整体之上的一致性机制,即所有参与的成员都以相同的顺序接收和应用提交的事务,因此,您还必须设置 --slave-preserve-commit-order=1 来确保并行事务的最终提交与原始事务的顺序相同。最后,为了确定哪些事务可以并行执行,中继日志必须还包含由--slave-parallel-type=LOGICAL_CLOCK生成的事务的父信息。尝试在不设置其它两个选项,只将 --slave-parallel-workers设为大于0的情况下,向组添加成员,会产生错误,实例也会被拒绝加入。

21.7.2 组复制的要求

以下是已知的组复制的限制。

  • 复制事件校验和  由于复制事件校验和的设计原因,组复制现在还不能利用它们,因此,设置 --binlog-checksum=NONE.

  • Gap Locks.  认证的过程没有考虑账户 锁的粒度,因为 在InnoDB以外,关于锁的粒度的信息都是不可用的。参阅 锁的粒度 获得更多信息。

    注意

    除非您的应用程序是依赖REPEATABLE READ 隔离级别,否则我们建议在组复制中使用 READ COMMITTED 隔离级别。在使用READ COMMITTED隔离级别时,InnoDB不会使用间隙锁(gap locks),这将InnoDB的本地冲突检测和组复制执行的分布式冲突检测对其。

  • 表锁和 Named 锁。 认证过程没有考虑表锁(参阅13.3.5, “锁表和解锁表语法”) 和 named 锁 (see GET_LOCK())。

  • SERIALIZABLE隔离级别。默认情况下,多主模式的组不支持 SERIALIZABLE隔离级别。 将事务隔离级别设置为SERIALIZABLE 来配置组复制,以拒绝提交事务。

  • 并发 DDL versus DML Operations.  当使用多主模式组复制时,不支持在不同的服务器上对同一对象并发的执行数据定义语句和数据操作语句。 在对一个对象执行DDL语句期间,在另一个服务器实例上对相同的对象执行并发的DML,与不同实例上,那个正在执行的DDL语句,产生潜在的冲突的风险。

  • 级联的外键约束。 多主模式的组(所有成员都配置了 group_replication_single_primary_mode=OFF) 不支持具有多级外键依赖关系的表。尤其是表被定义了 CASCADING外键约束。 这是因为外键约束,会导致 多主模式的组,执行的级联操作,会导致冲突不会被发现,这样就导致组员之间的数据不一致。因此,我们建议在多主模式的组中服务器的实例设置 group_replication_enforce_update_everywhere_checks=ON ,以避免未被发现的冲突。

    在单主模式下,就没有这个问题了,因为不允许对组中的多个成员进行并发写入,因此不存在冲突未被发现的风险。

  • 超大事务 。 个别事务导致GTID内容非常大,以至于不能在5秒内通过网络在组员之间复制,这样就可能导致组通信失败。为了避免这样的问题,尽可能的限制事务的大小。例如,将 LOAD DATA INFILE使用的文件拆分为更小的。

21.8 常见问答

这部分提供了常见问题的答案。

1、一个组最多有多少个MySQL服务器?

一个组最多含有9个服务器,继续请求加入服务器会被拒绝。

2、组中的服务器是如果连接的?

组中的服务器通TCP点对点连接到组中的其他服务器。这些连接只用于内部通信和组间服务器之间的信息传递。

3、group_replication_bootstrap_group 选项有什么用?

引导标志指示成员创建一个组,并充当初始的种子服务器。第二个服务器成员加入组时,需要询问种子成员,并以动态的修改配置,以便于第二个成员加入组。

在两个场景下才需要成员引导组,一个是组初始创建的时候,一个是整个组关闭后重启。

4、我如何为恢复过程设置认证?

可以使用CHANGE MASTER TO语句,预先配置组复制的恢复通道认证。

5、我可以使用组复制来扩展我的写负载吗?

不能直接用,但是MySQL组复制是一个全共享的完整复制解决方案,其中所有的成员服务器都复制相同数量的数据,因此,如果该组中的一个成员将N个字节写入到存储,以事务提交,那么也会将大约N个字节写入到其他成员的存储中,因为事务被复制到任何地方。

但是,考虑到其他成员不需要执行原始成员在最初执行事务时必须执行相同数量的过程,它们会更快捷的应用更改,因为事务以一种用于仅应用行转换的格式进行复制。而不必再次重新执行事务(基于行的格式)。

此外,考虑到这种更改的传输和应用是基于行的格式,这就意味着,它们是以被优化和紧凑的格式接收,并与原始成员相比,可能会减少IO。

总的来说,您可以通过将冲突自由的分散到组中的不同成员,来扩展处理能力,而且您还可以扩展IO性能,因为远程服务器只接收必要的更改操作。

6、在相同的工作量下,组复制相比于简单复制,是否需要更多网络带宽和CPU?

需要,因为服务器之间需要不断地进行交互以实现同步,但是很难量化,因为这还取决于组的大小(3个服务器的组对带宽的要求要比9个服务器的组需要的少).

此外,占用的内存和CPU更多,因为服务器的同步是一个复杂的工作内容。

7、我能在广域网部署组复制吗?

可以,但是每个成员之间的网络必须稳定且性能合适。最佳性能的要求就是低延迟、高带宽的网络连接。

如果确定网络带宽的问题,那么 21.9.7.2, “消息压缩”的使用可以降低带宽的要求。但是,如果网络掉包,导致重连,或更严重的是点对点之间的延迟、吞吐量以及整体延迟等,这些负面影响。

警告

组中成员之间,任何两者之间的网络往返时间(RTT)在2秒或更长,那么您可能会遇到问题,因为内置的故障检测机制可能会被错误的触发。

8、如果存在临时连接问题,成员会自动重新加入一个组吗?

这取决于连接问题的原因。如果连通性问题是暂时性的,并且重连足够快,以至于故障检测器还没有意识到它,那么服务器可能就不会从组中移除。如果是“长”连接性问题,那么故障检测器最终怀疑这个问题,那么服务器就会从组中移除。

一旦服务器从组中移除,,您需要再次将其加入。换句话说,就是一个服务器被显式的从组中移除之后,您需要手动(或者用脚本自动执行)的将其重新加入到组。

9、什么时候会排挤一个成员?

如果成员一直没有回应,其他成员会将其从组配置中删除。在实践中,这种情况可能发生在成员崩溃或网络断开的情况下。

如果超过了给定的响应时间,或新的配置中没有这个“沉默的”成员,就会认为这个成员失败了。

10、当一个节点明显落后时会怎样?

没有方法可以定义何时将成员自动从组中排除的策略。相反,您需要找出这个成员为什么会落后,并解决这个问题,或者将成员从组中移除。否则,如果服务器的速度非常慢,以至于它触发了流控制,那么整个组也会慢下来。可以根据您的需要配置流控制。

11、怀疑组中存在问题,是否存在一个特殊成员负责触发重新配置?

没有,组中没有特殊的成员来触发重新配置。

任何成员都可以怀疑有问题。所有成员都需要(自动地)同意某个成员已经失败了。其中一个成员负责通过触发重新配置来将其从组中删除。负责驱逐成员的成员不是你能控制或设置的东西。

12、组复制可以用于分片吗?

组复制被设计为提供高度可用的副本集;数据和写在组中的每个成员上都是重复的。为了超越单个系统所能提供的范围,您需要一个基于多个组复制集构建的编排和分片框架,其中每个副本集维护和管理一个给定的碎片或您的全部数据集的分区。这种类型的设置,通常被称为“分片集群”,允许您对读和写进行线性扩展,并且没有限制。

13、如何结合SELinux使用组复制?

如果开启了 SELinux,可以使用sestatus -v验证,然后您需要开启组复制通信端口,给mysqld配置 group_replication_local_address,以便绑定给它,并且监听。查看当前允许MySQL使用的端口,执行系统命令 semanage port -l | grep mysqld,假设端口配置为6606,那么就将这个端口添加到SELinux允许的端口中,执行命令: semanage port -a -t mysqld_port_t -p tcp 6606

14、如何结合iptables使用组复制?

如果iptables开启了,那么您需要打开组复制成员服务器之间互信的端口。查看本地当前服务器的规则,运行系统命令 iptables -L,如果端口配置为6606,那么开启这个端口进行通信的命令为: iptables -A INPUT -p tcp --dport 6606 -j ACCEPT

15、如何使用组复制通道中的中继日志来恢复一个组成员?

组复制所使用的复制通道的方式与在主复制通道中使用的复制通道相同,都依赖于中继日志。如果更改了relay_log变量,或者没有设置这个选项,并且主机名更改时,则有可能出现错误。参阅 18.2.4.1, “Slave 中继日志”获取恢复过程。与此同时,另一种解决方法就是在组复制中执行 STOP GROUP_REPLICATION 语句,然后一个 START GROUP_REPLICATION语句来重启实例。组复制插件会再次创建group_replication_applier 通道。

21.9 组复制技术细节

本节提供了关于MySQL组复制更多的技术细节。

21.9.1 组复制插件结构

MySQL组复制是一个MySQL插件,是建立在现有的MySQL复制架构上。利用二进制日志、基于行的日志记录和全局事务标识符等特性。它与当前的MySQL框架集成,例如性能模式或插件和服务基础结构,下图展示的就是MySQL组复制整体架构的块图。

图21.9 组复制插件块图

Group Replication Plugin Block Diagram

图的最上面(灰色框),是一组 APIs,用于控制组复制插件如何与MySQL服务器交互 。一些接口可以使信息从服务器流向插件,反之亦然。这样的接口将服务器核心与插件隔离开来,主要是在事务执行管道中进行限制(官方说直白说法就是:设置钩子。hooks)。还是那个位置, 从服务器到插件,这个方向上,有一些通知,比如服务器正在启动,服务器正在恢复,服务器准备接受连接,服务器准备提交事务,等等。反向的,组复制插件指示服务器提交或中止正在进行的事务,在中继日志中进行队列事务,等等。

在API板块的下面是些组件,当接到路由的通知就开始工作。组件capture负责捕获正在被执行事务的上下文;组件applier 负责在数据库上执行远程的事务;组件recovery 负责管理分布式恢复,以及负责集合通过轮询供体而加入的服务器。安排恢复过程以及对供体失败的反馈。

继续往下,复制协议模块包含复制协议的特定逻辑。它处理冲突检测、接收和传递到组的事务。

第一个绿色方框是组通讯API,它是一个高级API,将机器的复制状态所需的属性抽象化。 因此,它将消息层的实现与插件的其余上层分离。MySQL组复制插件包含一个基于Paxos的组通信引擎的实现。

21.9.2 组

在MySQL组复制中,一组服务器形成一个复制组。每个组都有一个UUID的形式名称。这个组是动态的,服务器可以离开(不管是自愿的,还是不自愿的),也可以随时加入。当服务器加入或离开时,该组会自动调整。

如果一个服务器加入组,那么它会自动的恢复缺失的数据到最新状态。这个状态是通过异步MySQL复制来传输的。如果一个服务器离开组,如,实例离线维护。那么其余的服务器会注意到它已经自动地离开并重新配置了这个组。 组成员关系服务的描述在21.1.3.2, “组成员关系”中有介绍。

21.9.3 数据操作语句

由于不存在主(masters)服务器,任何时候组中的每个服务器都可以执行事务,即使是改变状态的事务(RW事务)。

任何服务器都可以在没有任何预先协调的情况下执行事务。但是,在提交时,它会与组中的其他服务器协调,以决定该事务的命运。这种协调有两个目的:(i)检查事务是否应该提交;(ii)传播更改,以便其他服务器也可以应用该事务。

当一个事务以原子性广播发送时,那么组中的所有服务器都接收事务,或者都接收。如果都接收,那么他们都会以相同的 以相同的顺序接收它们,这个顺序是相对于之前事务被发送时的顺序。通过检查和比较事务的写集了来进行冲突检测。因为是基于行级检测,所以在第一个提交者获得胜后,冲突就解决了。举个例子。假如t1和t2在不同的节点同时执行,因为t2的顺序在t1之前,并且两者更改相同的行,那么t2就赢得冲突,t1被中止。换句话说,就是t1试图改变已经被t2变陈旧的数据。

注意

如果两个事务经常发生冲突,那么有个好的解决办法就是在同一个服务器上启动它们。然后,它们有机会在本地锁管理器上同步,这样就不会在复制协议中中止。

21.9.4 数据定义语句

在执行数据定义语句时需要注意,由于MySQL不支持原子性或事务行的DDL,所以不能随便地执行DDL语句,或执行语句后又回滚。因此,原子性的缺乏并不与组复制所基于的乐观复制模式直接匹配。

因此,在执行数据定义语句时需要格外注意。Schema的变化及其对象所包含数据的变化,需要通过相同的服务器处理。如果不这样做,当Schema操作还没有完成,以及复制到每个地方,就可能导致数据不一致。

注意

如果部署的是单主模式,那么就不存在这个问题,因为所有的变化的执行都是来自同一个服务器,主服务器。

警告

MySQL DDL 语句的执行不是原子性的或事务性的。服务器执行和提交,而不会先保护组协议。基于这样的原因,您必须通过相同的服务器为同一对象路由DDL和DML语句。这时候,虽然DDL语句在执行,但是却还没复制到其他地方。

21.9.5 分布式恢复

本节描述一个连接成员赶上其它服务器的过程,称为分布式恢复阶段。

21.9.5.1 组复制基础知识

组复制的分布式恢复过程可以总结为:一个新服务器获取来与组中其它成员之间缺失的数据的过程,同时监听组中发生的事件,在恢复期间,服务器会侦听成员事件,以及在恢复发生时正在发生的事务。下面的部分通过描述过程的两个阶段提供了详细的内容。

阶段 1

在第一阶段,加入者(添加的服务器),将会从组中选择一个已在线的服务器,作为缺失数据的 供体。从加入到组的那一刻起,供体将负责将处理加入者缺失的所有数据,这个过程是一个标准的异步复制通道来实现的,通过这个通道是建立在供体和加入者之间,通过这个管道,二进制日志一直向前流动,直到加入者成为组的一部分视图发送变化时。而且,加入者在接收来自供体的二进制日志时,还应用了它们。

此外,当二进制日志传输的同时,加入者也在缓存组内交换的每个事务。即,它一边侦听加入组后发生的事务,一边应用从供体传来的二进制日志。当第一阶段完成,然后通向供体的复制通道将关闭,加入者就开始第二阶段: 同步状态

阶段 2

在这个阶段,加入者将执行缓存的事务,当需要被执行的缓存事务数量为零时,该成员就被声明为在线(online)。

弹性恢复

如果加入者正在获取二进制日志时,它所依靠的供体不可用,发生故障,这种情况,无论何时阶段1中的供体发生故障,加入者会故障转移到一个新的供体,然后重新获取。当这个情况发生时,加入者会显式地关闭与故障供体的连接,并打开一个与新供体的连接。这些都是自动进行的。

21.9.5.2 时间点恢复

为了 是加入者和供体同步到特定的时间点,加入者和供体使用了MySQL全局事务标示符(GTIDs)机制。然而,这还不够。因为这只提供了一个平均值来实现加入者 。它并没有帮助标记一个特定的加入者需要恢复到的时间点。而且它也不能帮助传递认证信息。现在就是发挥二进制日志视图标记的作用的时候了,通过在二进制日志流中标记视图的变更,还包含其它元数据信息,以便给加入者提供认证相关的数据。

21.9.5.2.1 视图和视图变更

为了解释视图变更标记的概念,很重要的一点就是理解视图和视图的变更是什么。

一个视图对应的是参与当前配置的一组成员,换句话说,在一个特定的时间点,他们在系统中是正常的以及online。

当对组配置进行修改时,就会出现视图变更,比如成员的加入或离开。组中任何成员改变都会导致一个独立的视图,在同一逻辑时间点上与所有成员进行通讯。独立的视图更改。

一个视图标示符唯一地标识一个视图,在视图变化是产生。

在组的通信层,视图变更 At the group communication layer, view changes with their associated view ids are then boundaries between the data exchanged before and after a member joins. This concept is implemented through a new binary log event: the"view change log event". The view id thus becomes a marker as well for transactions transmitted before and after changes happen in the group membership.

The view identifier itself is built from two parts: (i) one that is randomly generated and (ii) a monotonically increasing integer. The first part is generated when the group is created, and remains unchanged while there is at least one member in the group. The second part is incremented every time a view change happens.

The reason for this heterogeneous pair that makes up the view id is the need to unambiguously mark group changes whenever a member joins or leaves but also whenever all members leave the group and no information remains of what view the group was in. In fact, the sole use of monotonic increasing identifiers could lead to the reuse of the same id after full group shutdowns, destroying the uniqueness of the binary log data markers that recovery depends on. To summarize, the first part identifies whenever the group was started from the beginning and the incremental part when the group changed from that point on.

21.9.5.3 视图变更

本节解释了控制视图变更标识符如何被合并到一个二进制日志事件并写入日志的过程,步骤如下:

开始: 稳定的组

所有的服务器都是在线,处理着来自组的事务。有些服务器可能在复制方面有点落后,但是最终它们会赶上来的。组充当一个分布式和复制的数据库。

图 21.10 稳定的组

Stable Group

视图变更:加入一个成员

无论什么时候新成员加入组时,都需要执行视图变更。每个在线服务器都排队执行视图更改日志中的事件。 之所以说是排队,因为在视图发生变更之前,可以在服务器上队列了数个事务,因此,这些事务属于旧视图。在执行完这些事件后,视图会发生变更,以保证当前的标记是正确的。

与此同时,加入者通过视图抽象出来的成员关系服务中,选择一个在线的服务器作为数据供体。加入的成员如下面的s4,在线成员将视图变更事件写入到二进制日志中。

图 21.11 加入一个成员

A member joins the group at view 4

状态转化: 同步状态

一旦加入者选择了供体,之间两者就建立了一个新的异步复制连接,状态就变为(阶段1)。 与供体的交互会一直持续到加入者的applier线程处理视图变更日志事件,这些事件是时加入者,加入组的那一刻之后触发的事件。换句话说,加入者从供体那里复制,直到加入者达到视图标识的位置,这个标识和加入者自身在视图中的标识位置是相匹配的。

图 21.12 状态转化: 同步状态

State transfer is executed

因为视图标识位置会以相同的逻辑时间传递给所有的成员,加入者知道到达什么标识时,就停止复制。这样就避免了复杂发GTID集计算,因为视图ID清楚标记这哪些数据属于哪个视图。

当加入者在从供体那里复制的时候,它还会缓存来自组的事务。最后,停止通供体那里复制后,转而进行对缓存事务的应用。

图 21.13 队列的事务

Incoming transactions from the current view are queued

最后:同步完成

有个重点需要理解,就是最终的恢复过程。当加入者从供体那里复制的时,识别到了视图变更日志事件中之前的变更标识。那么与供体之间的通道就会关闭,然后开始应用缓存的事务。 尽管变更标识,二进制日志中充当标记手的角色,对视图变更划定界限,它还扮演者另外一个角色,就是当有加入者时,它把认证信息传递给了所有的服务器。换句话说就是,如果没有它,加入者就没有必要的信息来证实(检测冲突)后续的事务。

状态同步(阶段2)的说持续的时间是不确定的,因为它取决于工作负载和对组传入事务的速度,这个过程是完全在线完成的。当在同步状态时,加入者不会阻塞组中的其他任何服务器。因此,基于以上原因,当它到了阶段2时,后续需要处理的事务数量的多少都是基于工作负载而变化的。

当加入者的队列事务为零,且储存的数据和其他成员相同时,它的对公状态就变为online(在线)。

图21.14 实例上线

Server has caught up

21.9.5.4 分布式恢复的使用建议和限制

分布式恢复有一些局限性,它是基于经典的异步复制,因此,如果没有提前对加入者进行部分数据恢复,或者恢复的数据是很久之前的备份,那么整个过程可能会很慢。这就意味着,如果在阶段1传输的数据量太大,那么服务器可能需要很长的时间才能恢复。因此,建议在向组添加成员服务器之前,应该使用对组中的一个成员服务器最新的快照。这样就最小化阶段1的时间,同时也减少了对供体服务器的影响,因为供体服务器只需保存和传输少量的二进制日志。

警告

建议在将一个服务器加入到组之前,先对加入者进行配置。这样,就可以最小化在恢复步骤上花费的时间。

21.9.6 可观察性

组复制插件中有很多内置的自动化功能,尽管如此,您也可能有时需要知道幕后发生的事情。这时候,组复制和 Performance Schema就变得尤为重要。系统的整个状态(包括视图,冲突统计和服务状态)都可以通过 performance_schema表来查询得到。复制协议的分布式特性,和服务器实例认同以及事务和元数据的同步的事实, ,使得对组状态的检查变得更加简单。举个例子,您可以连接扫组中的一台服务器,通过对组复制相关的Performance Schema表的查询,来获取本地和全局信息。更多信息,参阅 21.3 节, “监控组复制”

21.9.7 组复制性能

本节将解释如何使用可用的配置选项,来使组获得最佳性能。

21.9.7.1 组通信线程微调

当加载组复制插件时,组通信线程(GCT)变循环运行。 GCT 接收来自组和插件的信息,处理仲裁和故障检测相关的事情,发送一些保持存活的信息,同样还处理 来自或去向,服务器或组,传入或传出的事务。 GCT 用队列的方式等待传入的信息,当没有信息时,GCT会等着。在某些情况下,通过延长等待(积极等待)的时间会有益的。原因在于操作系统需要将处理器切换到GCT,进行上下文切换。

为了强制GCT 积极等待。可以使用 group_replication_poll_spin_loops 选项。使得GCT在为了获得下一个消息,而轮询队列之前,一直循环,而不要对已配置的循环数量做任何相关的操作。 在实际轮询队列获得下一个消息之前,配置的循环次数不做任何操作,一直循环。

举个例子:

mysql> SET GLOBAL group_replication_poll_spin_loops= 10000;

21.9.7.2 消息压缩

当网络带宽是一个瓶颈时,消息压缩可以在组通信级别对吞吐量提供高30-40%。这对大型组,且组的服务器处于负载时的上下文的传出特别有用。

表 21.8 LZ4 不同二进制日志格式的压缩比

Ratio

Workload

ROW

STMT

mysqlslapd

4,5

4,1

sysbench

3,4

2,9


组中N个参与者之间的相互TCP点对点性质的连接,让发送方,发送相同量的数据N次。此外,二进制日志会显示展现出高压缩比(见上表)。 这使得在含有大型事务的工作负载下,压缩成为一个引人注目的特性。

图21.15 支持的压缩

Compression Support in the Group Communication Service

压缩发生在组通信引擎级别,是在数据被传递给组通信线程之前,因此它发生在mysql用户会话线程的上下文中。 将事务负载发送到组之前压缩,并在接收时进行解压。压缩是有条件的,并且取决于配置的阈值,默认情况下,压缩是启用的。

此外,没有必要为了能够协同工作,而要求组中所有的服务器进行压缩。一旦接受到消息,成员会检查消息封面是否是压缩的。根据需要的话,成员将事务提交到上一层之前,对其解压。

压缩默认启用的,而且压缩算法使用的是LZ4,阈值是1000000字节。压缩阈值是以字节为标准。可以设置的比默认值大,这种情况下,只有大于阈值的事务才会被压缩。下面举例,设置压缩阈值。

STOP GROUP_REPLICATION;
SET GLOBAL group_replication_compression_threshold= 2097152;
START GROUP_REPLICATION;

上面的将压缩阈值设置为2MB,如果一个事务产生的复制信息负载大于2MB,如,二进制日志事务条目大于2MB,那么他将被压缩。设置为0,就是禁用压缩。

21.9.7.3 流控制

组复制确保事务只有在组中大多数成员都接收,并就所有并发发送的事务之间的相对顺序上达成一致时,才会提交事务。

这种方式适用于,如果对组的写总数没有超过组中任何接收写成员的能力,如果确实是这样的,并且一些成员的写吞吐量比其他成员的差,尤其是比写服务器的差,那么这些成员就开始落后与写服务器。

让一些成员落后与组会带来一些问题,尤其是,那些成员中提供读的,可能会显示非常老的数据。依据成员落后的情况,其他成员可能需要保存更多或更少的复制上下文,以便能够实现来自慢成员的潜在数据请求。

但是,在复制协议中存在一种机制,以避免在快成员和慢成员之间存在太大的事务差,这就大家知道的流控制机制。它试图解决几个目标:

  1. 保持成员之间可以缓存足够的距离,以及成员之间同步问题;

  2. 为了适应快速变化的环境,如,不同的工作负载,或组中更多的写服务器;

  3. 让每个成员公平的分担部分可用的写作能力;

  4. 不要为了避免浪费资源而减少吞吐量。

考虑到组复制的设计,决定是否进行节流时,需要考虑到两个工作队列: (i) 认证 队列; (ii) 二进制日志上的 applier 队列。不论何时,只要其中某个队列的大小超过了用户自定义的阈值时,就会触发节流机制。仅配置: (i) 是否在 certifier或applier级别进行流控制,或两者都配置;以及(ii)每个队列的阈值。

流控制取决于两个基本的机制:

  1. 对成员进行监控,收集所有组成员的吞吐量和队列大小的一些统计信息,以便对每个成员所应承受的最大的写压力进行推测;

  2. 那些每时每刻,都试图超出其可分担能力的成员的节流。

21.9.7.3.1 调查和统计

监视机制的工作原理,是让每个成员部署一组探针来收集关于其工作队列和吞吐量的信息。然后,它会定期将这些信息传播给组,以便与其他成员共享该数据。

这样的探测散布在插件堆栈中,并允许建立度量标准,例如:

  • certifier队列的大小;

  • 复制applier队列的大小;

  • 已认证事务的总数;

  • 成员已应用远程事务的总数;

  • 本地事务的总数;

一旦成员接受到来自其他成员的统计信息,它就会计算上一个监控周期中额外的指标,有多少个事务被认证、应用以及本地执行了多少个事务,等。

监控数据定期与组中的其他成员共享。监视周期必须足够长,以允许其他成员决定当前的写请求,但是也要足够短,以便对组带宽的影响最小。这些信息是实时共享的,且这个周期的长短必须足以解决这两个问题。

21.9.7.3.2 组复制节流

根据组中所有服务器上收集的度量标准,然后启用节流机制,决定是否限制一个成员执行/提交新事务的速度。

因此,从所有成员获得的度量标准,来计算每个成员能力的基础:如果一个成员有一个大的队列(用于认证或applier线程),那么执行新事务的能力应该接近于在上一周期中的认证或应用能力。

组中所有成员中能力最低的成员,是组的实际能力。而本地事务的数量决定了有多少个成员正在向其写入。 从而,有多少个成员可以与之分担 。

这就意味着,每个成员都有一个基于其能力而建立的写配额,换句话说,下个周期可以安全的执行多少事务。如果certifier或二进制日志applier的序列大小超过了自定义的阈值,那么写能力的配额就会被节流机制强制执行。

配额减少了上一个周期中延迟的事务量,然后会进一步缩减10%,以便允许队列触发问题来缩减其大小。 为了避免队列大小在超过阈值后,吞吐量大幅增加,吞吐量只允许每个周期增长10%。

当前的节流机制不会对低于限额的事务有影响,但会延迟完成那些超过它的事务,直到监控期结束。因此,如果对写请求的配额非常小,那么一些事务可能会延迟到监视期间。