理解因果集群仲裁和集群恢复
几个主要的因果集群操作需要大多数集群成员在线,即大多数仲裁。当因果集群失去大多数仲裁时,它将失去写入能力以及添加或删除集群成员的能力。
本文解释了这种行为的原因,以及它如何影响从某些集群问题中恢复。
仲裁提交和数据持久性
当我们失去仲裁时,集群进入只读状态,并且不再拥有执行写入操作的领导者。
此行为是支持因果集群的 Raft 协议的一部分,旨在确保数据持久性和维护数据库 ACID 性。
由于我们有仲裁提交,当仲裁丢失时,在最坏的情况下,拥有最新数据的仲裁节点都离线,并且没有一个在线节点拥有最新的提交。
在恢复仲裁并确保集群反映最新提交的数据之前,允许写入操作将是一个错误。
仲裁投票进/出和集群恢复
仲裁也需要投票进出集群成员,这会影响集群规模扩展和集群恢复的可用选项。
请注意,集群成员资格与集群节点的在线或离线状态是分开的。一个节点有可能成为集群的成员,但又无法访问或离线。
虽然无法访问或离线的节点可能会触发投票出尝试以将其从集群成员资格中删除,但投票出操作有两个要求
-
集群成员的成员资格计数高于
causal_clustering.minimum_core_cluster_size_at_runtime
。 -
存在大多数仲裁以通过投票。
如关于集群规模扩展的文章中所述,集群可以优雅地缩减规模,直至配置的 causal_clustering.minimum_core_cluster_size_at_runtime
(默认值为 3),前提是保持仲裁。
随着集群规模的缩减(或扩大),大多数仲裁所需的在线节点数量将根据集群成员资格的大小而变化。
当我们失去仲裁时,必须恢复导致仲裁丢失的离线/无响应节点以重新获得仲裁。请记住,即使这些成员可能处于离线状态,它们仍然被计为集群的成员,因为由于仲裁丢失,它们从未被投票出局。
我们不能添加新的集群成员以恢复仲裁,因为需要仲裁才能投票加入新的集群成员。
虽然这似乎限制了我们不能简单地向集群添加新节点以恢复正常操作,但此行为仍然支持数据持久性。
在上一节中讨论的最坏情况下,当仲裁丢失时,有可能只有离线节点包含最新提交的数据(由于仲裁提交)。
如果我们错误地允许添加新节点以恢复仲裁,我们可以恢复写入操作,但数据将与离线成员一起丢失。即使我们稍后恢复这些成员,在此期间,新的提交可能与丢失的数据冲突,导致数据重复或不一致,并且没有明确的方法来解决冲突。
要求仲裁进行提交和集群投票进/出操作对于维护集群中的数据持久性是必要的。
投票进/出操作的日志消息示例
Raft 成员资格操作显示在调试日志中,以下是一些您可能看到的示例。
我们正在使用一个最多包含 6 个核心成员的因果集群。对于此配置,我们分别为每个成员使用 5001 到 5006 的发现端口,以及 7001 到 7006 的 Raft 监听地址。
此时,我们有 4 个核心节点在线,分别对应于节点 1、2、3 和 5。此时,我们尝试启动节点 6。
在调试日志中,我们将看到多个调试消息,但这些消息特别显示了新成员的发现和(成功!)尝试将新成员添加到集群中
2019-03-29 21:57:59.670+0000 INFO [o.n.c.d.CoreMonitor] Discovered core member at localhost:5006 2019-03-29 21:57:59.676+0000 INFO [c.n.c.d.SslHazelcastCoreTopologyService] Core topology changed {added=[{memberId=MemberId{416eca31}, info=CoreServerInfo{raftServer=localhost:7006, catchupServer=localhost:6006, clientConnectorAddresses=bolt://localhost:7667,http://localhost:7464,https://localhost:7463, groups=[], database=default, refuseToBeLeader=false}}], removed=[]} 2019-03-29 21:57:59.676+0000 INFO [o.n.c.c.c.m.RaftMembershipManager] Target membership: [MemberId{1b45d851}, MemberId{cf17e1cf}, MemberId{2011b3fb}, MemberId{3e6dbf35}, MemberId{416eca31}] ... 2019-03-29 21:58:14.806+0000 INFO [o.n.c.c.c.m.RaftMembershipManager] Getting consensus on new voting member set [MemberId{2011b3fb}, MemberId{3e6dbf35}, MemberId{1b45d851}, MemberId{cf17e1cf}, MemberId{416eca31}] 2019-03-29 21:58:14.806+0000 INFO [o.n.c.c.c.m.RaftMembershipChanger] ConsensusInProgress{} 2019-03-29 21:58:14.813+0000 INFO [o.n.c.c.c.m.RaftMembershipManager] Appending new member set RaftMembershipState{committed=MembershipEntry{logIndex=5, members=[MemberId{2011b3fb}, MemberId{3e6dbf35}, MemberId{1b45d851}, MemberId{cf17e1cf}]}, appended=MembershipEntry{logIndex=6, members=[MemberId{2011b3fb}, MemberId{3e6dbf35}, MemberId{1b45d851}, MemberId{cf17e1cf}, MemberId{416eca31}]}, ordinal=7}
如果我们停止节点 6,我们将看到成员集相应地发生变化,因为集群的其余部分将节点 6 投票出集群
2019-03-29 22:09:40.191+0000 WARN [o.n.c.d.CoreMonitor] Lost core member at localhost:5006 2019-03-29 22:09:40.203+0000 INFO [c.n.c.d.SslHazelcastCoreTopologyService] Core topology changed {added=[], removed=[{memberId=MemberId{416eca31}, info=CoreServerInfo{raftServer=localhost:7006, catchupServer=localhost:6006, clientConnectorAddresses=bolt://localhost:7667,http://localhost:7464,https://localhost:7463, groups=[], database=default, refuseToBeLeader=false}}]} 2019-03-29 22:09:40.203+0000 INFO [o.n.c.c.c.m.RaftMembershipManager] Target membership: [MemberId{1b45d851}, MemberId{cf17e1cf}, MemberId{2011b3fb}, MemberId{3e6dbf35}] 2019-03-29 22:09:40.204+0000 INFO [o.n.c.c.c.m.RaftMembershipManager] Getting consensus on new voting member set [MemberId{2011b3fb}, MemberId{3e6dbf35}, MemberId{1b45d851}, MemberId{cf17e1cf}] 2019-03-29 22:09:40.204+0000 INFO [o.n.c.c.c.m.RaftMembershipChanger] ConsensusInProgress{} 2019-03-29 22:09:40.209+0000 INFO [o.n.c.m.RaftOutbound] No address found for MemberId{416eca31}, probably because the member has been shut down. 2019-03-29 22:09:40.209+0000 INFO [o.n.c.c.c.m.RaftMembershipManager] Appending new member set RaftMembershipState{committed=MembershipEntry{logIndex=6, members=[MemberId{2011b3fb}, MemberId{3e6dbf35}, MemberId{1b45d851}, MemberId{cf17e1cf}, MemberId{416eca31}]}, appended=MembershipEntry{logIndex=7, members=[MemberId{2011b3fb}, MemberId{3e6dbf35}, MemberId{1b45d851}, MemberId{cf17e1cf}]}, ordinal=9}
在这两种情况下,您都可以看到成员资格更改是作为一致性提交来实现的。
在降至运行时最小核心大小以下时的日志消息示例
在此场景中,节点 1、3 和 5 处于 3 节点集群的运行状态。我们将停止节点 3 并观察日志消息
2019-03-29 22:12:20.280+0000 WARN [o.n.c.d.CoreMonitor] Lost core member at localhost:5003 2019-03-29 22:12:20.281+0000 INFO [c.n.c.d.SslHazelcastCoreTopologyService] Core topology changed {added=[], removed=[{memberId=MemberId{cf17e1cf}, info=CoreServerInfo{raftServer=localhost:7003, catchupServer=localhost:6003, clientConnectorAddresses=bolt://localhost:7637,http://localhost:7434,https://localhost:7433, groups=[], database=default, refuseToBeLeader=false}}]} 2019-03-29 22:12:20.281+0000 INFO [o.n.c.c.c.m.RaftMembershipManager] Target membership: [MemberId{1b45d851}, MemberId{3e6dbf35}] 2019-03-29 22:12:20.281+0000 INFO [o.n.c.c.c.m.RaftMembershipManager] Not safe to remove member [MemberId{cf17e1cf}] because it would reduce the number of voting members below the expected cluster size of 3. Voting members: [MemberId{3e6dbf35}, MemberId{1b45d851}, MemberId{cf17e1cf}]
2 个节点的目标成员资格无法实现,因为这会将投票成员的数量减少到 3 个运行时集群大小以下。因此,即使节点 3 离线,它也不能从成员资格集中删除。仲裁大小保持在 2/3 个节点以实现大多数,因为我们保持 2 个集群节点在线。在日志中,我们可能会看到对离线节点的连接尝试,因为即使离线,它仍被视为集群成员。
如果我们再删除一个节点,我们将失去仲裁
2019-03-29 22:23:05.055+0000 WARN [o.n.c.d.CoreMonitor] Lost core member at localhost:5005 2019-03-29 22:23:05.056+0000 INFO [c.n.c.d.SslHazelcastCoreTopologyService] Core topology changed {added=[], removed=[{memberId=MemberId{1b45d851}, info=CoreServerInfo{raftServer=localhost:7005, catchupServer=localhost:6005, clientConnectorAddresses=bolt://localhost:7657,http://localhost:7454,https://localhost:7453, groups=[], database=default, refuseToBeLeader=false}}]} 2019-03-29 22:23:05.056+0000 INFO [o.n.c.c.c.m.RaftMembershipManager] Target membership: [MemberId{3e6dbf35}] 2019-03-29 22:23:05.056+0000 INFO [o.n.c.c.c.m.RaftMembershipManager] Not safe to remove members [MemberId{1b45d851}, MemberId{cf17e1cf}] because it would reduce the number of voting members below the expected cluster size of 3. Voting members: [MemberId{3e6dbf35}, MemberId{1b45d851}, MemberId{cf17e1cf}]
我们可能会看到这伴随着降级事件,因为如果没有仲裁,我们就无法拥有领导者或写入能力
2019-03-29 22:23:20.100+0000 INFO [o.n.c.c.c.s.RaftState] Leader changed from MemberId{3e6dbf35} to null ... 2019-03-29 22:23:20.101+0000 INFO [o.n.c.c.r.RaftReplicator] Lost previous leader 'MemberId{3e6dbf35}'. Currently no available leader 2019-03-29 22:23:20.101+0000 INFO [c.n.c.d.SslHazelcastCoreTopologyService] Step down event detected. This topology member, with MemberId MemberId{3e6dbf35}, was leader in term 2, now moving to follower.
我们可能会看到这伴随着对两个离线集群成员的连接尝试,以及由于仲裁不存在而导致的选举失败。
让我们看看如果我们此时尝试启动节点 6 会发生什么。请记住,节点 6 已经被投票出集群,因此它不被视为集群成员,并且集群成员的仲裁不存在,因此我们应该看到加入尝试失败
2019-03-29 22:29:41.003+0000 INFO [o.n.c.d.CoreMonitor] Discovered core member at localhost:5006 2019-03-29 22:29:41.004+0000 INFO [c.n.c.d.SslHazelcastCoreTopologyService] Core topology changed {added=[{memberId=MemberId{416eca31}, info=CoreServerInfo{raftServer=localhost:7006, catchupServer=localhost:6006, clientConnectorAddresses=bolt://localhost:7667,http://localhost:7464,https://localhost:7463, groups=[], database=default, refuseToBeLeader=false}}], removed=[]} 2019-03-29 22:29:41.004+0000 INFO [o.n.c.c.c.m.RaftMembershipManager] Target membership: [MemberId{3e6dbf35}, MemberId{416eca31}]
拓扑和目标成员资格消息显示新节点已被检测到并希望加入集群,但我们没有看到关于新的投票成员集的一致性消息或使用已提交的 MembershipEntry 附加新的成员集。
由于我们没有看到一致性消息或提交成员资格条目消息,因此我们知道节点 6 无法成功加入集群。
如果我们恢复其中一个离线成员节点并通过使大多数集群成员在线来重新建立仲裁,我们将看到一致性和已提交的成员资格条目消息恢复,因为仲裁将允许集群再次开始投票进出集群成员,并且仲裁大小将根据成员资格集的变化而相应变化。
neo4j-admin unbind 如何影响因果集群恢复
因果集群问题的几个恢复过程需要使用 neo4j-admin unbind
,它会删除集群状态,并且通常与删除、覆盖(例如从还原操作中)或移动实例上的 graph.db 结合执行。由于图数据正在更改,集群状态不再反映存储的状态,因此 unbind 操作是必要的。
了解这如何影响集群成员资格以及在某些情况下这如何影响集群恢复非常重要
其集群状态已被解除绑定的节点不能用于恢复集群的仲裁(以及写入操作)。
当您执行 neo4j-admin unbind
时,由于该节点的集群状态被销毁,它在集群中的标识也将被销毁。当节点重新联机时,它将拥有一个新的成员 ID,并且在集群中将显示为一个全新的节点。
如果集群当前拥有大多数集群成员在线的仲裁,那么应该不会产生负面影响,因为仲裁应该允许新恢复的节点被投票加入集群。
但如果集群没有仲裁,那么新恢复的节点将无法被投票加入,也无法有助于稳定集群。即使恢复的节点的先前成员 ID 仍然被计为集群成员(尚未被投票出集群),该节点也无法假定其先前的标识,也无法将其自身传递为当前成员。并且允许这种情况发生是不正确的。
此行为是预期的,并且是 Raft 实现的一部分,有助于数据持久性。如果没有此行为,那么可能会出现导致集群中数据丢失或数据冲突的场景。
当集群失去仲裁时,恢复它的唯一方法是让当前的离线成员之一(尚未被投票出局)重新加入集群,并且在成员上不能使用 neo4j-admin unbind
。
如果仲裁已丢失,并且无法在不使用 neo4j-admin unbind
的情况下使任何当前离线成员重新联机,那么您可能被迫回退到完整的集群恢复过程,这将需要使集群离线。
此页面是否有帮助?