Group Replication Operations - malongshuai/MySQL-Group-Replication GitHub Wiki

4.Group Replication Operations

组复制操作

本节介绍部署组复制的不同模式,说明管理组的常用操作并提供有关如何调整组的内容。

4.1 Deploying in Multi-Primary or Single-Primary Mode

部署多主或单主模型

组复制有以下两种模型:

  • 单主模型(single-primary)
  • 多主模型(multi-primary)

默认模型为single-primary。组中部署不同的模型是不现实的,例如一个节点配置为单主模型,而另外一个节点配置为多主模型。要想切换工作模型,需要整个组(而非节点)都重启并应用对应模型的配置。无论是哪种工作模型,组复制都不会处理client端的故障转移,这种问题只能在应用程序自身来处理,或者使用连接器或中间件(例如路由、代理)来处理。

当部署为多主模型时,将会检查语句以确保它们能兼容这种模型。以下是部署为多主模型时需要做的检查:

  • 当在SERIALIZABLE隔离级别下执行事务时,那么当它自身和组进行同步时,提交将失败。
  • 如果事务涉及操作具有外键级联约束的表时,那么当它自身与组进行同步时,提交将失败。

可通过设置选项group_replication_enforce_update_everywhere_checks为FALSE来禁用这些检测。如果部署为单主模型,该选项必须设置为FALSE。

4.1.1 Single-Primary Mode

单主模型

该模型下只有一个主节点被设置为read-write,组中剩余所有节点都设置为read-only(如superread-only)。这些设置是部署为单主模型时自动设置的。

该模型下的主节点一般是第一个启动用来引导组的节点,其余所有后续加入组的节点会自动学习该设置,并设置为read-only。

但使用单主模型时,会禁用一些多主模型下的检测,因为系统强制某一时刻只允许单节点写操作。例如,允许修改具有外键级联约束的表,而这在多主模型下是不允许的。当主节点故障后,主节点自动选举机制将会自动选出另一个主节点。在选举主节点时,将查找新的成员视图,并根据group_replication_member_weigth的值排列出潜在的主节点,如果所有成员的MySQL版本都相同, 则权重最高的节点被选举为下一个主节点,如果权重值相同,则根据字典顺序对它们的server_uuid进行排序,然后选出列表中的第一个节点作为下一个主节点。当选举出新的主节点后,该主节点将自动设置为read-write,其他节点继续作为slave,且保留设置为read-only。

如果MySQL版本不支持group_replication_member_weigth,那么将根据server_uuid的字典顺序来选举新节点。

在将客户端应用程序重新路由到新节点之前,等待新节点应用完relay-log是一个好习惯。

4.1.2 Multi-Primary Mode

多主模型

在多主模型下,没有单主的概念,没有必须进行选举的过程,因为多主模型下没有节点扮演特殊的角色。

当节点加入组时,所有节点都会设置为 read-write。

4.1.3 Finding the Primary

查找主节点

下面的示例演示了如何查询单主模型下当前的主节点。

mysql> SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE
VARIABLE_NAME='group_replication_primary_member';
+--------------------------------------+
| VARIABLE_VALUE                       |
+--------------------------------------+
| 69e1a3b8-8397-11e6-8e67-bf68cbc061a4 |
+--------------------------------------+

或者:

mysql> SHOW STATUS LIKE 'group_replication_primary_member'

4.2 Tuning Recovery

调整恢复过程

当一个新成员要加入复制组,它会连接组中一个合适的信息供应者(donor),并从它身上抓取它所缺失的那部分数据。这是组复制中的一个关键组件,它是可容错的、可配置的。下一节将解释恢复过程是如何工作的以及如何调整相关设置。

选择Donor

Donor是从组中在线成员中随机选择的。这种方式可以降低某成员被多个同时加入的成员选中为Donor的几率。

如果连接到选定的donor失败,则会自动尝试选择一个候选donor来连接。当连接失败次数达到最高限制数量后,恢复过程将失败并报错。

注意
donor是从当前视图中的在线节点列表中随机挑选的。

Donor自动切换

另一个恢复过程中需要关注的关键点是对待失败的处理方式。组复制为此提供了健壮的错误探测机制。在组复制的早期版本中,当新成员联系donor时,恢复过程只能探测到因为认证问题或其他一些问题而导致的连接错误。对于这些问题,会切换到一个新的donor上,并因此而尝试和另一个成员建立连接。

以下这些错误也会选择另一个donor:

  • Purged data scenarios - 当donor上已purge过一段日志,但新节点上没有这段日志对应的数据,新节点就无法获取完整得到数据,这时候恢复过程就会报错,并重新选择一个新的donor。
  • Duplicated data - 当待加入的成员已有一部分数据,但和选中的donor上准备复制到自身的数据有冲突时,恢复过程会发生错误。这可能是因为待加入成员上存在一些不合理的事务造成的。有人可能会说,这样的问题应该要直接失败而不是切换到另一个donor,但是有时候有一些奇怪的复制组,可能会有其他成员共享了冲突的事务。基于此,发生上面的错误后,恢复过程会选择另一个donor而不是直接失败。
  • Other errors - 如果恢复过程的线程失败(receiver或applier线程),也会错误并选择另一个donor。

重连Donor

恢复过程的传输依赖于donor上的二进制日志以及MySQL复制框架,因此传输过程中的任何一个错误都回导致receiver和applier线程错误。这种情况下,donor切换具有重试功能,就像常规复制一样。

Donor重试次数

待加组成员重试donor的次数为10。可通过插件变量group_replication_recovery_retry_count修改重试次数。下面的命令设置最大重试次数为10。

SET GLOBAL group_replication_recovery_retry_count= 10;

Sleep Routines

插件变量group_replication_recovery_reconnect_interval用于设置两次连接donor的时间间隔(译注:见下面的解释)。该变量默认值为60秒,可动态修改。下面的命令设置该时间间隔为120秒。

SET GLOBAL group_replication_recovery_reconnect_interval= 120;

但注意,这个时间间隔不是连续两次选donor时的时间间隔,而是所有donor都轮遍了后还没有选好donor时,恢复过程进入睡眠的时间。所以,它影响的不是在同一个轮询内选donor的时间间隔,因为每次所选的donor都是不同节点。

4.3 Network Partitioning

网络分裂(网络分区, Network Partitioning)

无论什么时候,组中发生了一个需要做复制的改变后,复制组都要对此改变达成一致。例如产生的常规事务就如此要求。此外,对于组成员变更以及其他一些内部消息也要求组内保持一致性。对于一个决定,要达到组内共识,需要组内大多数成员都同意此决定。当组内在线成员数达不到"大多数"的要求,也就是达不到法定票数,则这个组中所有需要做决定的操作都会被阻塞。

当多个成员非自愿离开组时,这表示组的大多数节点突然间离开组,而非自愿离开组的成员的法定票数会丢失(译注:等价于弃权),这样就无法满足大多数的要求。例如,一个5节点的组,如果3个节点突然沉默(不心跳),这样剩下2个节点就不可能达到5的大多数(3)。实际上,3节点突然离组或者网络分裂将2个节点隔离出去,这两个节点是无法通告任何消息的,因此也不能做任何自动配置、协调操作(译注:失去大多数节点后,剩余的少数节点永远无法自动调整,组的大小永远都是5,这样2个节点永远也满足不了大多数)。

另一方面,如果节点自愿离开组,它们会告诉组让它们重新配置组成员。实际上这意味着节点告诉组,我要离开了,你们重组吧。例如上面的5节点组,突然3个要自愿离开,在它们相继离开后,剩余的2个节点重新配置后,它们两独自组成一个组。这时的组成员总数是2而不是5,所以组中的2是组的全部,意味着达到大多数的要求。(译注:自愿离开时,会自动配置组大小,配置好之后才会成功离开组,这样5节点的组离开3个自愿退出的节点后 ,组的大小为2,而不是5)

下面将介绍出现网络分区而无法满足大多数要求时如何解决。

4.3.1 Detecting Partitions

探测分区

performance schema中的replication_group_members表可以显示当前视图中每个节点的状态(以本节点为观察角度)。大多数时候,复制组中不会出现分区现象,所以该表显示的组中成员信息是完全一致的。也就是说,每个节点的状态都是经过当前视图中所有节点协商后同意的。但是,如果出现了网络分裂,那些无法联系到的主机的法定票数会丢失,它们在表中显示的状态将是UNREACHABLE。该信息会被内置在组复制中的本地故障探测器向外通告。

为了理解这种类型的网络分裂,下面将解释上图的场景,初始时5节点正常工作,然后突然做出改变,使得组中只剩2个在线节点。

因此,先假定有一个包含5个节点的组:

  • 节点 s1的标识符为 199b2df7-4aaf-11e6-bb16-28b2bd168d07
  • 节点 s2的标识符为 199bb88e-4aaf-11e6-babe-28b2bd168d07
  • 节点 s3的标识符为 1999b9fb-4aaf-11e6-bb54-28b2bd168d07
  • 节点 s4的标识符为 19ab72fc-4aaf-11e6-bb51-28b2bd168d07
  • 节点 s5的标识符为 19b33846-4aaf-11e6-ba81-28b2bd168d07

初始时,所有节点都正常工作,它们也都能正常地通信。你可以连接到 s1 上通过它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 | 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       |
+---------------------------+--------------------------------------+-------------+-------------+--------------+

但随后在 s3、s4 和 s5 上发生了意料之外的灾难性事故。几秒之后,再去查看 s1 实例上的表replication_group_members,结果发现 s1 自身是 ONLINE 的,但 s3、s4 和 s5 却UNREACHABLE,如下。此时组将无法自动重新配置它们自身来调整组中的成员信息,因为无法满足组的大多数要求。

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  |
+---------------------------+--------------------------------------+-------------+-------------+--------------+

该表显示 s1 现在处于一个没有外部干预就无法继续处理的组中,因为大多数节点都是 unreachable 状态。在这种特殊的情况下,组成员列表需要进行重置以便让祖复制继续工作。或者,你也可以停止实例 s1 和 s2 上的组复制(或直接停止它们的MySQL服务),然后查出 s3、s4 和 s5 上发生了什么问题,最后重启组复制(或MySQL服务)。

4.3.2 Unblocking a Partition

疏通网络分裂导致的阻塞

组复制允许你通过强制配置的方式重置组成员列表。例如上面的情况, s1 和 s2 是组中仅剩的在线成员,你可以强制配置组使其只由 s1 和 s2 组成。这需要检查 s1 和 s2 上的一些信息,然后再使用变量group_replication_force_members

现在回到s1和s2是组中仅剩节点的情况。服务器s3、s4和s5意外地离开了组,要使服务器s1和s2继续运行,你需要强制配置一个只包含s1和s2的组。

警告
此处使用的group_replication_force_members应当将其认为是最后的补救手段。你必须非常小心地使用它,并只有在无法仲裁的情况下使用。如果使用不当,则可能会人为地造成脑裂或阻塞整个复制系统。

回想一下,目前复制系统被阻塞,当前的配置如下(由 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  |
+---------------------------+--------------------------------------+-------------+-------------+--------------+

第一件事就是获取s1和s2对端的地址(组通信的标识)。连接上s1和s2,如下是s1上获取的信息:

mysql> SELECT @@group_replication_local_address;
+-----------------------------------+
| @@group_replication_local_address |
+-----------------------------------+
| 127.0.0.1:10000                   |
+-----------------------------------+

连上 s2 做相同操作:

mysql> SELECT @@group_replication_local_address;
+-----------------------------------+
| @@group_replication_local_address |
+-----------------------------------+
| 127.0.0.1:10001                   |
+-----------------------------------+

当你知道了它们用于成员间通信的地址s1(127.0.0.1:10000)、s2(127.0.0.1:10001)后,你可以使用它们中之一来注入一个新的组成员配置,然后覆盖已存在且失去仲裁能力的那个组成员配置。例如,在 s1 上操作:

mysql> SET GLOBAL group_replication_force_members="127.0.0.1:10000,127.0.0.1:10001";

这表示强制配置新的组成员配置。然后,在 s1 和 s2 上查看表replication_group_members来验证组成员信息是否更改。首先在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       |
+---------------------------+--------------------------------------+-------------+-------------+--------------+

然后在 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       |
+---------------------------+--------------------------------------+-------------+-------------+--------------+

当强制配置新的组成员配置时,需要确保那些被排除在外的节点确实是已经停止工作的。在上面的场景中,如果 s3, s4 和 s5 并非真的是unreachable而是ONLINE,这3个节点将自己组成一个分区,且这个分区还正好满足组的大多数要求。这种情况下,强制创建只包含 s1 和 s2 的组将会人为造成脑裂的问题。因此,在重新配置 s1 和 s2 为新的组成员之前,先确认其他节点是否确实故障是非常重要的,如果它们之中还有非故障节点,但你还想继续创建新的组成员配置,请手动停止这些节点。