次要成员在 MongoDB 的副本集中确认什么?

DRo*_*elt 5 nosql replication mongodb

我试图更好地理解MongoDB 副本功能的机制和行为。我希望从熟悉 mongo 内部和行为的人那里得到一些有见地的信息。我的目标是了解 Mongo 在高可用性设置中的持久性保证。我想要维护的数据将具有唯一约束(通过使用两个唯一索引),通过在一个文档中具有两个唯一值来维护 1:1 关系。不一致或仅部分持久性对我的 1:1 关系来说是致命的,一旦发生就很难解决,尤其是未被发现。

我的问题:

辅助节点在副本集设置中确认并使用 writeconcern:"majority" 究竟是什么?

仅在次要语句中确认并应用该语句,还是应用整个 oplog 直到应用新语句?

解释我到底在寻找什么:

根据他们自己的文档和其他一些来源(例如 Aphyr 尝试模拟网络分区及其后续),使用写关注“多数”将确保,即使在分区场景等中也不会丢失任何写入。(因为我不能发布两个以上的链接,我将无法引用 mongodb 的文档)。在我的用例中,陈旧的读取是可以接受的。

但是我从他们的文档中无法弄清楚的一件事是,复制品上究竟承认了什么。

副本集通过异步分发使用 oplog(修改数据的语句集合)进行复制。为了避免在失败的主尝试通​​过写入加入集群时回滚,这些写入不是分布式的(由于异步性质),他们建议使用多数写入关注(Doc->Replica->Replica Concepts->High Availbilty->回滚)。据我了解,在某些情况下,这无法实现,这取决于确切承认的内容。

假设我们有三个实例,(1)、(2)、(3)。一个是主要的 (1,P),另外两个是次要的 (2,S), (3,S)。

我们现在正在尝试插入两个新行/文档 {1} 和 {2}。并且由于网络不稳定,有时实例之间将不可用。

假设副本集确认来自主节点的单个语句,那么可能会发生以下情况:

  1. 实例 (3,S) 不可用,但插入记录 {1} 会成功,因为 3 个中有 2 个可用(达到多数)。

当前状态:

(1,P):{{1}}

(2,S):{{1}}

(3,S):{{}}不可达

  1. 实例 (2,S) 变得不可用,并且实例 (3,S) 恰好在线(未重新启动,只是对其他人在线)。同时,主节点没有启动异步复制。插入记录{2}。再次获得多数票(3 人中有 2 人)

当前状态:

(1,P):{{1},{2}}

(2,S):{{1}}不可达

(3,S):{{2}}

如果 (1,P) 下降,就会发生选举。(2) 或 (3) 将成为主要的。但这会使数据库不一致,因为新的主数据库不知道其他丢失的文档/行。现在,当原始主数据库上线(从而丢失数据)时,必然会发生某种回滚。

如果通过应用所有最新的 oplog 条目进行确认,则不应发生这种情况。(2,S) 在确认 {2} 时将具有两个条目。And then will also win the election. 数据不会丢失。

我很清楚这不太可能发生。但是,如果发生这种情况,除非仅禁用自动故障转移,否则我什至看不到它是如何被检测到或可以避免的。

Mar*_*erg 4

好吧,虽然问题相当复杂,但答案却非常简单,尽管有点长。

复制(简化)

当操作保存在 oplog 中时,它不会分发给其他成员。它由副本集成员拉取。

重新加入副本集的失败成员将联系主节点并拉取其尚未处理的 oplog 条目。所以它最终会变得一致。

但是,有一个警告。出于性能原因,oplog 是一个有上限的集合。这意味着可用空间有限。由于空间有限,可写入 oplog 的操作数量也有限。并且有限的操作只能回溯有限的时间,因为当新条目超过 oplog 大小限制时,oplog 中最旧的条目将被覆盖。有限的时间范围称为复制 oplog 窗口。

如果成员重新加入集群并且其最后一个操作早于主操作日志中最旧的条目,则它会变得陈旧并且需要完全重新同步数据。

所以在你的例子中,在 3,S 宕机后,2,S 仍然会很高兴地拉取 oplog。在您的示例中,所有三个都在短时间内启动,直到 2,S 失败。现在将进行选举,并且由于无需更改主要 1,P 仍将担任此职能。

现在,当 3,S 在 oplog 窗口中出现时,它会简单地连接到主节点,拉取 oplog,将 oplog 条目应用到其数据集,从此以后一切都过得很幸福(管理员除外,他必须回来)再次移动到 2,S 以实现双冗余)。

如果情况 3,S 出现在 oplog 窗口之外,您必须通过以下方式初始化完全重新同步

  1. 关闭实例mongod
  2. 擦拭其dbpath
  3. 重启实例

请注意,如果 2,S 在此过程中未启动并重新同步,则集群不会有多数启动和运行,因此不可写。

确认操作

对于 的写入关注{w:1},主节点将确认该查询是

  1. 已收到并
  2. 语法正确,因为它是
  3. 应用于data和oplog的内存数据集

对于 的写入关注{w:1,j:1},上述所有内容均适用。此外,修改会写入日志,从而保证数据的持久性 - 除了日志的边缘情况被损坏之外。

请记住,操作会每隔commitIntervalMs写入日志,默认情况下为 100 毫秒,是写入关注度为 的配置值的三分之一{"w":x, "j":1}。我们稍后再讨论这个问题。

通过上面详述的复制过程,for x > 1 的工作方式不同就足够了{"w":x, "j":1},因为这样可以保证主数据库拥有所有副本集成员的最新数据:对于上述写关注,仅确认对日志的写入对于初级。

从客户端的角度来看,副本集要么可写(大多数非隐藏、非延迟成员已启动),要么不可写。如果可写,则主节点保证拥有最新数据,并且辅助节点故障恢复将自动从 oplog 重新同步或需要完全重新同步。

边缘案例{w:1}

然而,有一个特殊的条件。对于写入问题,{w:1}可能会发生主节点即将失败并接受未到达辅助节点的写入的情况。当这台机器重新加入副本集时,它会注意到它的最新写入操作比它尚未处理的新主操作日志中最旧的操作要新。然后,实例将识别它已接受但未到达辅助节点的所有写入操作,将它们写入rollback中的文件夹dbpath,最大限制为 300MB。

处理回滚可能非常棘手,但是很容易预防: set w > 1

经验法则:w < 2除非您能承受丢失数据的损失,否则切勿设置

结论

通过写入关注w<1,您可以防止回滚,并且可以非常确定写入操作是持久的。如果所有副本集成员的数据应该在 commitIntervalMs 的时间范围内失败,那么它就不会持久,因此(对于默认值)在最坏的情况下为 100 毫秒(所有日志都在完全相同的点写入)时间),在最好的情况下为 50 毫秒(日志恰好移动 50 毫秒),这给了我们平均 75 毫秒的时间,其中所有受影响的副本集成员都必须失败。如果这对您来说太多了,请添加更多副本集成员,降低所有应该处理操作的服务器失败的可能性,或者设置j:1. 不过,这仅适用于副本集成员同时失败的情况w。对于地理上分布的副本集,这种情况的概率在所有实际用途中都等于 0。如果僵尸启示录与小绿色外星人的入侵同时发生,我认为你的担忧(没有双关语)将会改变。

如果写入操作在确认后写入磁盘非常重要,那么在认为写入操作成功之前,您仍然可以采取强制文件同步的大锤。