分布式恢复

分布式恢复即一个成员加入或重新加入复制组时,它必须跟上其它组成员的事务应用。加入的成员首先检查其group_replication_applier通道的relay log,以查看它已经从组收到但还未应用的事务。如果加入的成员以前在组中,它可能会在找到离开之前未应用的事务,在这种情况,它将应用这些事务作为第一步。在此之后,加入的成员连接到一个正常的成员进行数据复制,加入的成员将加入之前或离开时在组中发生的事务数据进行复制。随后加入的成员应用数据复制过程中发生的事务,完成后加入的成员即可正常参与到群组中。

在分布式恢复期间,组复制结合下列方法进行传输:

  • MySQL 8.0.17使用clone插件的函数进行远程克隆操作,需提前安装该插件。Review
  • 使用group_replication_recovery异步复制通道从源节点的二进制日志进行复制,并应用事务,直到二进制日志记录加入成员加入组(视图更改事件)。此过程中,加入的成员会缓冲组应用的新事务,当二进制日志传输完成后应用缓冲的事务

在加入成员上执行START GROUP_REPLICATION后,组复制自动根据情况选择最佳传输方式,如果加入的成员和源节点的事务间隙较大或者二进制日志缺乏必要的事务,则组复制通过远程克隆的方式进行恢复。如果没有安装较大事务间隙或没有安装克隆插件,则组复制通过源节点的二进制日志进行恢复。

分布式恢复连接

分布式恢复连接与组复制通信了解不同

  • 组通信引擎采用Xcom(paxos的变体)的连接用于实例之间的TCP通信,该连接由group_replication_local_address变量指定
  • 8.0.20之前,组成员提供标准SQL客户端连接来加入成员;8.0.21开始,组成员可以发布一个分布式恢复端点列表,作为加入成员的专用客户端连接,通过group_replication_advertise_recovery_endpoints变量指定列表。

加入成员创建分布式恢复连接的步骤如下:

  1. 当成员加入组时,它连接group_replication_group_seeds列表中的一个成员,默认为group_replication_local_address的成员
  2. 向加入成员提供复制内在线的所有成员列表,内容包含分布式恢复端点或每个成员为分布式恢复提供的SQL客户端
  3. 加入成员从列表中选择一个合适的成员作为数据捐赠者
  4. 加入成员尝试使用捐赠者发布的分布式恢复端点连接,并按照列表顺序依次尝试,如果没有提供恢复端点则采用SQL客户端进行连接
  5. 如果加入成员不能连接到选定的捐赠者,它会重新选择其它合适的捐赠者。当加入成员在没有建立连接的情况下耗尽恢复端点,它不会退回到SQL客户端,而是直接换一个捐赠者

对于二进制日志的恢复方式,组复制限制了加入的成员试图连接捐赠者的次数,如果达到限制没有连接成功,则恢复终止。需要注意的是,这个限制指定了加入成员总的尝试次数。默认连接次数限制为10,可以通过group_replication_recovery_retry_count变量进行配置

mysql> SET GLOBAL group_replication_recovery_retry_count= 5;

group_replication_recovery_reconnect_interval变量定义了分布式恢复进程在连接尝试的间隔时间。需要注意的是,它不会每次连接尝试后休眠,而是经过所有可能的捐赠者之后才会暂停,单位为秒。参数默认值为60秒,可以动态修改该值

mysql> SET GLOBAL group_replication_recovery_reconnect_interval= 120;

从MySQL8.0.18开始,可以为分布式恢复选择压缩方式,压缩可以在网络带宽有限并需要传输较多事务时带来不错的收益。group_replication_recovery_compression_algorithm和group_replication_recovery_zstd_compression_level系统变量配置允许的压缩算法和zstd压缩级别。但这些压缩设置不适用于远程克隆操作,当使用克隆作为分布式恢复的方法,clone插件的clone_enable_compression设置将生效。

用于分布式恢复的SSL与组通信的SSL是分开配置的,由MySQL SSL配置和group_replication_ssl_mode变量决定。默认情况下,分布式恢复不启用SSL,并且用户认证插件为caching_sha2_password,需要通过group_replication_recovery_public_key_path变量指定RSA公钥文件或者设置group_replication_recovery_get_public_key从捐赠者请求公钥;如果想要启用SSL,可以设置group_replication_recovery_use_ssl=ON启用,组复制将此设置应用于远程克隆操作,以及来自捐赠者的二进制日志的状态传输。

克隆恢复

MySQL8.0.17开始支持克隆插件,如果要在组中使用远程克隆操作进行分布式恢复,则必须预先设置现有成员和加入成员,以支持此功能。并且至少需要在加入成员和捐赠者上安装克隆插件,为复制用户赋予backup_admin权限,并将group_replication_clone_threshold变量设置为适当的级别。

需要注意的是,远程克隆会先将加入成员本地的数据清空!!!

关于克隆插件的安装配置与使用,可参考MySQL8.0新特性:Clone Plugin。对于组复制,注意以下关键点和异同:

  • 捐赠者(现有成员)和接收者(加入成员)必须安装和激活克隆插件
  • 捐赠者和接收者必须运行在相同的操作系统上,并且必须有相同的MySQL版本
  • 捐赠者和接收者必须安装和激活组复制插件,并且在捐赠者上激活的其它插件也必须在接收者上激活
  • 克隆插件有许多系统变量来控制网络负载和远程克隆对性能的影响
  • 如果在启动组复制之前使用change master或change replication提供用户凭据,请确保在进行任何克隆操作之前删除用户凭据,还要确保在加入成员上设置了group_replication_start_on_boot=OFF。如果未取消设置用户凭据,则在远程克隆期间将它们转移到加入成员

当创建了支持克隆的组成员时,group_replication_clone_threshold系统变量设置事务数,如果捐赠者的事务和加入成员上的事务之间差距大于该变量,则在允许的情况下,使用远程克隆转移到加入成员。组复制根据现有成员的gtid_executed集合计算是否超过阈值。该变量默认值非常高(GTID中允许的事务的最大序列号),因此在二进制日志转移会禁用克隆,可以根据需求来手动调整进行克隆。但不能设置的过低,否则在克隆期间可能会多次触发阈值不停的发起克隆操作。

root@(none) 22:17:  show variables like 'group_replication_clone_threshold';
+-----------------------------------+---------------------+
| Variable_name | Value |
+-----------------------------------+---------------------+
| group_replication_clone_threshold | 9223372036854775807 |
+-----------------------------------+---------------------+

当无法从捐赠者二进制日志进行传输(例如,不存在需要的二进制日志),组复制将尝试执行远程克隆,而不受group_replication_clone_threshold变量限制。

组复制启动并管理用于分布式恢复的克隆操作,为支持克隆而建立的组成员也可以参与用户手动发起的克隆操作。例如,你希望通过从组成员克隆出一个新的实例,但不希望实例立即加入组复制或不加入组复制。需要注意的是接收者即使不加入组复制,也需要安装激活组复制插件。

在MySQL8.0.20之前,克隆设计组复制成员则不能手动发起克隆操作。在MySQL8.0.20中,只要克隆操作不删除和替换接收者的数据,就可以做到这一点。因此,手动启动克隆的语句必须包含DATA DIRECTORY子句。

CLONE INSTANCE FROM 'user'@'host':port IDENTIFIED BY 'password' [DATA DIRECTORY [=] 'clone_dir'] [REQUIRE [NO] SSL];

分布式恢复的工作机制

当分布式恢复过程从二进制日志中执行状态转移时,使用GTID将加入成员同步到特定时间点。然而,gtid只是提供了一种方式来实现连接成员失去了哪些事务,无助于标记加入复制组的成员必须赶上的特定时间点,也不能传递认证信息。这需要通过binary log视图来实现。

视图对应一组正常在线的组复制成员,当对组复制配置进行修改时(成员加入或离开),视图将发生修改。任何组成员关系的更改都会导致同一逻辑时间点上与所有组成员通信的独立视图更改。视图标识符唯一标识一个视图,每当视图发生修改,就会生成它。

在组通信层,视图修改及其关联的视图标识符标记了成员加入之前和之后交换的数据之间的边界。这是通过一个二进制日志事件实现的:view change log event。视图标识符被记录下,用来界定组成员关系发生更改之前和之后传输的事务。
视图标识符本身包含两部分:随机生成的部分和单调递增的整数。随机部分是在创建组的时候产生的,并且在组内至少有一个成员时保持不变;每次视图发生变化时,整数部分就是进行递增。使用这两个不同的部分使视图标识符能够识别成员加入或离开所导致的增量组更改,还可以识别在完全关闭组时所有成员离开组的情况

1、稳定的组复制
所有组成员都在线联机处理组复制传递的事务
Stable Group

2、成员加入
成员加入时执行视图变更,每个在线成员都将视图更改日志事件排队等待执行,因为在此之前可能存在未应用的事务。同时,加入的成员通过视图抽象从成员服务声明的在线服务器列表中选择合适的捐赠者。成员在VC4加入,在线成员将视图更改事件写入二进制日志。
A Member Joins

3、数据传输
如果组成员和加入成员安装配置了克隆插件,加入成员之间的的事务差异超过了group_replication_clone_threshold的阈值,则进行分布式恢复远程克隆操作。如果需要的事务存在与任何现有成员的二进制日志文件中,也会进行远程克隆操作。当远程克隆完成并重新启动加入成员时,将从捐赠者的二进制日志进行传输来应用远程克隆期间的事务。如果没有较大的事务间隙或者没有安装克隆插件,则组复制直接从捐赠者的二进制日志进行传输。
Catching Up

由于视图标识符在同一逻辑时间传输给组中的所有成员,加入组的成员知道它应该在哪个视图标识符处停止复制,避免了复杂的GTID计算。

当加入成员从捐助者复制时,它也缓存来自组的传入事务。最终,它停止从捐赠者复制,并切换到应用缓存的那些事务。
Queued Transactions

4、联机处理
当加入成员识别出带有预期视图标识符的视图更改日志事件时,断开捐赠者连接,并开始应用缓存的事务。此外,当加入组的成员联机时,视图标识符传递了所有成员所感知的认证信息,即最后一个视图更改。如果没有它,加入成员将没有必要信息对后续事务进行冲突检测。

当加入成员达到零滞后事务且存储数据与其它成员相等时,成员状态切换为online,正式加入复制组。
Instance Online

网络分区

当复制发生变化时,组内需要达成共识。共识需要组内大多数成员同意做出的决策,当组内大多数成员意外故障失去联系,仲裁丢失,复制组无法判断故障的大多数成员是崩溃还是网络分隔,因此复制组将停滞不前。

另一方面,如果是成员主动退出,在退出前将通知复制组重新配置成员列表,并重新调整仲裁成员数量。

对于系统分区导致组内成员无法自动实现仲裁,该按下列步骤进行处理:

  • 检测分区
    performance schema下的replication_group_members表以当前节点为视角,展示当前视图下各个成员的状态,正常情况下成员状态都一致。如果存在网络分区,并且仲裁丢失,那么将能列出不可达的成员信息
    Losing Quorum

    mysql> SELECT MEMBER_ID,MEMBER_STATE FROM performance_schema.replication_group_members;
    +--------------------------------------+--------------+
    | MEMBER_ID | MEMBER_STATE |
    +--------------------------------------+--------------+
    | 1999b9fb-4aaf-11e6-bb54-28b2bd168d07 | UNREACHABLE |
    | 199b2df7-4aaf-11e6-bb16-28b2bd168d07 | ONLINE |
    | 199bb88e-4aaf-11e6-babe-28b2bd168d07 | ONLINE |
    | 19ab72fc-4aaf-11e6-bb51-28b2bd168d07 | UNREACHABLE |
    | 19b33846-4aaf-11e6-ba81-28b2bd168d07 | UNREACHABLE |
    +--------------------------------------+--------------+
  • 重置组成员
    对于组复制,可以强制指定配置来重置组成员列表。例如当前只有S1和S2存活,可以通过参数group_replication_force_members配置S1和S2的信息强制形成一个新的复制组,其中仅包含S1和S2。
    Forcing a New Membership

Warning:这种方式应该被视为最后的补救措施,谨慎使用。需要先评估丢失的大多数仲裁成员是由于网络隔离还是节点故障导致的,否则将存在两个复制组,形成人为的脑裂。

mysql> SET GLOBAL group_replication_force_members="S1,S2";
mysql> SELECT MEMBER_ID,MEMBER_STATE FROM performance_schema.replication_group_members;
+--------------------------------------+--------------+
| MEMBER_ID | MEMBER_STATE |
+--------------------------------------+--------------+
| b5ffe505-4ab6-11e6-b04b-28b2bd168d07 | ONLINE |
| b60907e7-4ab6-11e6-afb7-28b2bd168d07 | ONLINE |
+--------------------------------------+--------------+

在使用group_replication_force_members强制创建新的复制组后,需要将group_replication_force_members变量清空再执行START GROUP_REPLICATION启动复制组。

IPv6支持

MySQL8.0.14开始,组复制成员可以使用IPv6地址作为组内通信IPv4的替代。要使用IPv6,操作系统和数据库都必须配置为支持IPv6。在group_replication_local_address参数指定IPv6地址并配置端口号时,IPv6地址必须用中括号括起来

group_replication_local_address= "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:33061"

group_replication_local_address中指定的网络地址或主机名用于组复制中的唯一标识,如果同时解析为IPv4和IPv6,则组复制连接总是使用IPv4。当IPv4和IPv6混合时,加入成员必须以group_replication_group_seeds中成员发布的协议进行初始化连接,无论该协议是IPv4还是IPv6。当加入成员group_replication_local_address参数有IPv4,复制组group_replication_group_seeds中任何成员都带有IPv6地址(或者相反)时,加入成员的allowlist必须设置为对应协议的地址。如果加入成员的allowlist没有对应协议的地址,则拒绝其连接尝试。

例如,Server A是组内的种子成员,采用IPv6地址,因此它在group_replication_group_seeds设置IPv6地址进行传播

group_replication_bootstrap_group=on
group_replication_local_address= "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:33061"
group_replication_group_seeds= "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:33061"

Server B是新加入的成员,采用IPv4地址

group_replication_bootstrap_group=off
group_replication_local_address= "203.0.113.21:33061"
group_replication_group_seeds= "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:33061"

Server B想成功加入组,其IPv4地址和IPv6地址都必须出现在group_replication_ip_allowlist中

group_replication_ip_allowlist="203.0.113.0/24,2001:db8:85a3:8d3:1319:8a2e:370:7348,
2001:db8:8b0:40:3d9c:cc43:e006:19e8"

Server B应该具有与Server A相同的allowlist