首先是一些背景:
mongodb 副本集使用操作日志进行复制。主节点的每次写入都附加到 oplog。
每个辅助节点查询其同步源以获取 oplog 条目,并批量检索和应用它们。
Oplog 条目是幂等的,必须按顺序应用。
在将条目写入 oplog 本身之前,它们首先被写入日志。
如果一个节点在应用批次时崩溃,那么从日志中重放整个批次是安全的,因为每个条目都是幂等的。
成员相互报告他们最近应用的 oplog 事件的标识符。
由于必须按顺序应用 oplog 事件,这意味着每个节点在报告条目之前已应用了所有条目,而此后则没有。
在一个 5 节点的副本集中,大多数是 3 个节点。主节点不会向客户端/应用程序确认w:majority
写入已完成,直到大多数节点报告其 oplog 已达到或超过添加写入的点。
副本集中的每个节点都监视其他每个成员。
如果主节点意识到它无法与大多数投票节点通信,它将降级到辅助节点。
如果辅助节点在几秒钟内(默认为 10 秒)与主节点没有任何通信,它将要求选举。
要求选举的节点将向每个副本集成员征求投票。
每个成员通常会投“是”,他们可能投“否”的几个原因是:
- 候选者最近的oplog条目不等于或比其他节点更近
- 节点当前与优先级等于或高于候选节点的主节点进行通信
如果候选节点收到足够的“是”票以构成副本集中的多数投票节点,则它会转换为主节点并接受写入。
每次选举都会增加term
副本集的值。If the primary receives a heartbeat or other message from one of the other nodes that contains a higher term than when it was elected, it immediately steps down to secondary.
在您的场景中:
{w:majority, j:true} 写入命中主节点。
初级会:
- 将写入应用到数据集的本地副本
- 将操作写入日志
- 将操作写入oplog
假设主节点的 oplog 现在包含:
时间 |
手术 |
T1 |
创建集合 |
T2 |
创建索引 |
T3 |
插入文档 1 |
T4 |
插入文档 2 |
T5 |
更新文件 1 |
T6 |
插入文档 3 |
我将使用 T5 的更新作为w:majority
写入。
辅助节点轮询更改,主节点等待多数确认。
- 辅助节点可能不会同时接收数据
- 每个辅助可能有不同的最后应用条目
- 辅助节点不会根据写入问题对 oplog 事件做出不同的响应(我认为辅助节点甚至不知道写入问题)
N1 是主要的,N5 是仲裁者。在 T5 写入之前,其余节点显示以下最近应用的 oplog 事件:
N2:T4
N3:T4
N4:T2
N2 确认更改。
最近应用的 oplog 事件现在是:
N2:T5
N3:T4
N4:T3
N3 已收到更改并正在应用中。
这意味着 N3 不会向任何其他节点报告新的最后应用 oplog 时间,并且主节点仍然不会向提交写入的应用程序进程确认成功或失败。
主要发生故障
在主要失败之前的那一刻,每个成员最后应用的 optime 是:
N1:T6
N2:T5
N3:T4
N4:T3
N5:不适用
N3 无法向 Primary 确认它已应用更改。
情况下合理。
选举是强制的。
在适当的超时之后,一个或多个辅助节点将要求选举。
让我们假设 N3 实际上确实完成了应用来自 T5 的操作。
如果 N4 恰好是第一个超时的,它将要求进行选举,并要求所有成员投票给它。投票结果如下:
N4:是(总是为自己投票)
N5:否 - 我可以看到一个节点的 oplog 事件比你拥有的更新
N3:否 - 我的 oplog 事件比你拥有的更新
N2:否 - 同上
此时 N2 或 N3 将成为下一个选举人,投票结果如下:
候选人 N3:是 - 自己
N5:是 - 你有最近的事件
N4:是 - 你比我
更新 N2:是 - 你至少和我一样更新
本次选举成功,节点转为主节点。
如果 N3 已应用来自 T5 的事件,则无论是否将其报告给主节点,都可能发生此事件序列。由于所有节点都监视所有其他节点,因此 N3 将向 N2、N4 和 N5 报告。
请注意,如果 #4 没有发生,N3 仍将处于 T4,并且不会被选举,因为 N2 有更近期的事件。
在任何一种情况下,都会保留参与选举的任何辅助节点应用的最新事件。
当 N1 恢复并尝试重新加入副本集时,新主节点上的 oplog 很可能已超出 T6。由于当前主节点不知道 T6 的写入,并且之前的主节点不能成为辅助节点,除非它可以重播当前主节点的 oplog 条目,因此节点会比较它们的 oplog 历史记录,直到找到一个共同点。
在这种情况下,这将是 T5。
然后,前一个主节点在 T6 回滚操作所做的更改,并将回滚的数据写入本地磁盘文件,以便以后可以在必要时恢复。然后它从新的主节点请求 T5 之后的 oplog 条目,并开始作为辅助节点进行复制。
在这种情况下,唯一丢失的写入是由主节点处理但未复制到任何其他节点的写入。
如果 Primary 已确认写入,并且 N2 复制了写入被多数确认的事实,结果是否会有所不同?如果 N2 和 N3 都复制了写入被多数人确认的事实,结果是否不同?
这个事实实际上不需要复制。每个节点都在监视其他节点,因此每个辅助节点都将清楚地知道哪些 oplog 条目已被大多数副本集看到,而不会被告知。
唯一的区别是,如果 N3 还向主节点报告它已复制操作,则主节点可能会向应用程序报告多数写入成功。
替代方案
如果:
- #4 没有发生
- 故障是网络分区而不是节点故障
- N1和N2在隔板的一侧,N3、N4、N5在另一侧
在这种情况下,N1 将意识到它无法联系到大多数副本集,并将下台。
如果 N1 或 N2 试图要求选举,他们将无法获得所需的 3 票
N4 would still not be elected due to being behind N3
当 N3 要求选举时,N4 和 N5 都会投票“是”,因此 N3 将成为主要成员。
这意味着当网络分区被修复时,N1 和 N2 都会发现它们有当前主节点不知道的写入。
在这种情况下,T5 和 T6 写入都将回滚。