理解因果集群法定人数和集群恢复
几个主要的因果集群操作需要大多数集群成员在线,即多数法定人数。当因果集群失去多数法定人数时,它将失去写入能力,以及添加或删除集群成员的能力。
本文解释了此行为的原因,以及它如何影响从某些集群问题中恢复。
法定人数提交和数据持久性
当我们失去法定人数时,集群进入只读状态,并且不再有领导者来执行写入操作。
此行为是支持因果集群的 Raft 协议的一部分,旨在确保数据持久性并维护数据库的 ACID 特性。
由于我们有法定人数提交,当法定人数丢失时,在最坏的情况下,拥有最新数据的法定人数节点都处于离线状态,并且所有在线节点都没有最新的提交。
在恢复法定人数并确保集群反映最新提交的数据之前,允许写入将是一个错误。
法定人数投票进/出和集群恢复
投票进出集群成员也需要法定人数,这会影响集群规模扩展和集群恢复的可用选项。
请注意,集群成员身份与集群节点的在线或离线状态是分开的。一个节点可能是集群的成员,但却无法访问或处于离线状态。
虽然无法访问或离线的节点可能会触发投票出局尝试,以将其从集群成员身份中移除,但投票出局操作有两个要求:
-
集群成员的数量高于
causal_clustering.minimum_core_cluster_size_at_runtime
。 -
存在多数法定人数以通过投票。
正如在关于集群规模扩展的文章中讨论的那样,只要保持法定人数,集群就可以平稳地将其规模缩小到配置的 causal_clustering.minimum_core_cluster_size_at_runtime
(默认 3)。
随着集群规模的缩小(或扩大!),多数法定人数所需的在线节点数量将根据集群成员身份规模而变化。
当我们失去法定人数时,那些离线/无响应并导致法定人数丢失的节点必须被恢复以重新获得法定人数。请记住,即使这些成员可能处于离线状态,它们仍然被视为集群的成员,因为它们从未因失去法定人数而被投票出局。
我们无法添加新的集群成员来恢复法定人数,因为投票加入新的集群成员需要法定人数。
虽然这可能看起来很受限,因为我们不能简单地向集群添加新节点来恢复正常操作,但此行为仍然支持数据持久性。
在上一节讨论的最坏情况下,当法定人数丢失时,可能只有离线节点包含最新提交的数据(由于法定人数提交)。
如果我们错误地允许添加新节点来恢复法定人数,我们可能会恢复写入操作,但数据将随离线成员一起丢失。即使我们稍后恢复这些成员,在此期间新的提交可能与丢失的数据冲突,导致数据重复或不一致,并且没有明确的方法来解决冲突。
提交和集群投票进出操作都需要法定人数,这是维护集群数据持久性所必需的。
投票进出操作的示例日志消息
Raft 成员身份操作会出现在调试日志中,这里有一些您可能会看到的示例。
我们正在使用一个最多包含 6 个核心成员的因果集群。对于此配置,我们分别为每个成员使用发现端口 5001 到 5006,并分别为 Raft 监听地址 7001 到 7006。
此时,我们有 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}
在这两种情况下,您都可以看到成员身份更改是作为共识提交来实现的。
运行时低于最小核心规模时的示例日志消息
在此场景中,对于一个 3 节点的集群,节点 1、3 和 5 正在运行。我们将停止节点 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
的情况下使任何当前离线成员重新上线,那么您可能被迫退回到完整的集群恢复过程,这将需要使集群离线。
此页面有帮助吗?