本文是翻译MySQL InnoDB Cluster – how to manage a split-brain situation[1]这篇文章,如有翻译不妥或不对的地方,敬请谅解与指正。请尊重原创和翻译劳动成果,转载的时候请注明出处。谢谢!
每次我展示MySQL InnoDB Cluster时,在创建集群的演示中,很多人都不明白为什么当我集群中已有2个成员时,我的集群还不能容忍任何故障。 事实上,当您创建MySQL InnoDB集群时,只要您添加了第二个实例,您就可以看到状态信息:
仲裁(Quorum)
[译者注释] 这里Quorum翻译成仲裁,直译是法定人数。个人认为翻译成仲裁更好一些。
这是为什么呢?这是因为,要成为网络主分区(网络主分区指包含服务的网络分区,在默认的单主模式中指具有主节点的网络分区)的一部分,您的网络分区必须达到大多数节点(法定人数)。在MySQL InnoDB集群(和许多其他集群解决方案一样)中,要达到法定人数,分区中的成员数量必须大于50%。
[译者注释]:这里所谓的网络主分区(primary partition),其实是指集群中的节点可能由于网络故障导致变成了2个或多个隔离区域。是一个网络拓扑概念。
因此,当我们已有2个节点时,如果两个服务器之间出现网络问题,集群将分裂为2个分区。每个分区将拥有总成员数量的 50%(2 个中的1个)。那么50% > 50% 吗?不!在MySQL InnoDB集群的环境下,没有一个网络分区会达到法定人数,也没有一个网络分区允许查询。这就是原因。
事实上,第一台服务器会发现它再也无法与第二台服务器通信了……但为什么呢?是第二台服务器宕机了吗?是不是网络接口出了问题?我们不知道,所以我们无法决定。
让我们看一下这个由3名成员组成的集群 (3/3 = 100%):
如果我们看一下cluster.status()命令的输出信息,我们可以看到,有3个节点时可以容忍一个节点出现故障:
现在让我们想象一下,我们遇到一个网络问题,它会隔离其中一个成员/节点:
我们可以在cluster.status()命令的输出信息中看到节点丢失: 只要一个分区仍然具有法定人数(2/3 = 66%,大于 50%),我们的集群就仍然能够提供交易服务。
这里我想介绍一个非常重要的概念,因为这并不总是显而易见的。InnoDB Cluster 和 Group Replication 中的集群概念是不同的。实际上,InnoDB Cluster 依赖于DBA使用 MySQL Shell创建的元数据。这些元数据描述了集群的设置方式。Group Replication 以不同的方式看待集群。它以上次检查时的状态以及现在的状态来看待集群……并更新该视图。这通常被称为世界观(view of the world)。 因此,在上面的示例中,InnoDB 集群看到 3 个节点:2 个在线,1 个丢失。对于组复制,在短时间内,网络分区中节点处于UNREACHABLE状态,几秒钟后,在被多数人从组中逐出后(因此只有在仍有多数人的情况下),该节点不再是集群的一部分。组大小现在是 2/2(2/2 而不是 2/3)。此信息可以通过performance_schema.replication_group_members获取。
如果我们的网络问题更加严重,并将我们的集群分成 3 个,如下图所示,那么集群将处于“离线”状态,因为这 3 个分区均未达到法定多数,即 1/3 = 33% (<50%):
在这种情况下,MySQL 服务将无法正常工作,直到人工修复该问题。
解决问题
当集群中不再有网络主分区时(如上例所示),DBA 需要恢复服务。和往常一样,MySQL 错误日志中已经有一些信息:
从消息中我们可以看到这正是我们在这里解释的情况/场景。我们可以从命令cluster.status()的输出信息看到集群被“阻止”了:
我们有两个解决方案来解决这个问题:
- 使用SQL和组复制变量
- 使用 MySQL Shell 的 adminAPI
使用SQL和组复制变量进行修复
手册[2]中解释了这个过程(组复制:网络分区)。 在DBA想要用来恢复服务的节点上,如果只剩下一个节点,我们可以使用全局变量group_replication_force_members,并使用您可以在group_replication_local_address找到的服务器的GCS地址(如果有多个服务器在线但未达到大多数,则应将所有服务器添加到此变量中):
请注意,最佳做法是关闭其他节点,以避免在强制仲裁过程中再次出现任何类型的冲突。
集群将再次可用。我们可以在错误日志中看到情况已解决:
当节点上线后不要忘记删除变量group_replication_force_members的值
当网络问题解决后,节点将尝试重新连接,但由于我们强制成员身份/资格,这些节点将被拒绝。您需要通过以下方式将其重新加入群组:
- 重新启动 mysqld服务
- 重新启动组复制(stop group_replication; start group_replication)
- 使用 MySQL Shell ( cluster.rejoinInstance())
使用 MySQL Shell的adminAPI
另一个选项是使用MySQL Shell的adminAPI。这当然是更好的选择!使用 AdminAPI,您甚至不需要知道用于 GCS 的端口即可恢复仲裁。 在下面的示例中,我们将使用名为mysql4的服务器来重新激活我们的集群:
并且当网络问题解决后,Shell 还可以用于重新加入其他实例(在本例中为mysql6):
结论
如果您遇到任何原因在 MySQL InnoDB 群集上失去仲裁的情况,请不要惊慌。选择要使用的节点(或仍可相互通信的节点列表),如果可以的话,请关闭或停止其他节点上的mysqld服务。然后 MySQL Shell再次帮助你,你可以使用 adminAPI 强制仲裁并用一个命令中重新激活您的群集!
Bonus
如果您想知道您的 MySQL 服务器是否属于网络主分区(占多数的分区),您可以运行以下命令:
或者使用addition_to_sys_GR.sql这个SQL来确认。
参考资料
[1]
1: https://lefred.be/content/mysql-innodb-cluster-how-to-manage-a-split-brain-situation/
[2]
2: https://dev.mysql.com/doc/refman/8.0/en/group-replication-network-partitioning.html