MongoDb副本集

Mongodb副本集

复制简介

复制是将数据的相同副本保留在多台服务器上的一种方法,建议将其用于所有生产部署中。即使一台或多台服务器停止运行,使用复制功能也可以确保应用程序正常运行和数据安全。

在 MongoDB 中,创建副本集(replica set)后就可以使用复制功能了。副本集是一组服务器,其中一个是用于处理写操作的主节点(primary),还有多个用于保存主节点的数据副本的从节点(secondary)。如果主节点崩溃了,则从节点会从其中选取出一个新的主节点。

如果使用复制功能时有一台服务器停止运行了,那么仍然可以从副本集中的其他服务器访问数据。如果服务器上的数据已损坏或无法访问,则可以从副本集中的其他成员中创建一份新的数据副本。

从节点可能会落后于主节点(延迟)而缺少最新的写入,所以默认情况下从节点会拒绝读请求,以防止应用程序意外读取过期数据。因此,如果尝试在从节点上查询,则会弹出一条表明它不是主节点的错误消息。这是为了防止应用程序意外地连接到从节点并读取过期数据。要是想在从节点上进行查询操作,可以设置一个“在从节点中读取是没问题的”标志

secondaryConn.setSlaveOk()

从节点不接受写操作。从节点只能通过复制功能写入数据,不接受客户端的写请求

如何设计副本集

这里先了解一下副本集中最重要的概念,即“大多数”(majority):选取主节点时需要由大多数决定,主节点只有在得到大多数支持时才能继续作为主节点,写操作被复制到大多数成员时就是安全的写操作。这里的大多数定义为“副本集中一半以上的成员”。

img

注意,副本集中有些成员停止运行或者不可用时,并不会影响“大多数”。大多数是基于副本集的配置来计算的。

假设有一个包含 5 个成员的副本集,其中 3 个成员不可用,2 个成员可以正常工作。这 2 个成员不能达到副本集大多数的要求(至少需要 3 个成员),因此它们无法选举出一个主节点。如果这 2 个成员中有一个是主节点,那么当它注意到无法得到大多数成员支持时,就会从主节点上退位。几秒后,副本集中会包含 2 个从节点和 3 个无法访问的成员。

img

为什么剩下的 2 个成员不能选举出主节点? 问题在于:其他 3 个成员实际上可能没有崩溃,而是因为网络故障而不可达,在这种情况下,左侧的 3 个成员将选举出一个主节点,因为它们可以达到副本集成员中的大多数(5 个成员中的 3 个)。在网络分区的情况下,我们不希望两边的网络各自选举出一个主节点,因为那样的话副本集就拥有 2 个主节点了。如果 2 个主节点都可以写入数据,那么整个副本集的数据就会发生混乱。只有达到“大多数”的情况下才能选举或者维持主节点,这是避免出现多个主节点的有效方式。

img

配置副本集时很重要的一点就是只能有一个主节点。例如,在拥有 5 个成员的副本集中,如果成员 1、2 和 3 位于一个数据中心,成员 4 和 5 位于另一个数据中心,那么第一个数据中心总是可以满足大多数的条件(两个数据中心之间比数据中心的内部成员之间更可能出现网络中断)。

下面是两种推荐的配置方式。

  • 将“大多数”成员放在一个数据中心,如图 10-2 所示。如果有一个主数据中心,而且你希望副本集的主节点总是位于主数据中心,那么这是一个比较好的配置。只要主数据中心正常运转,就会有一个主节点。不过,如果主数据中心不可用了,那么备份数据中心的成员将无法选举出主节点。

  • 在两个数据中心各自放置数量相等的成员,在第三个地方放置一个用于打破僵局的副本集成员。如果两个数据中心“同等”重要,那么这种配置会比较好,因为任意一个数据中心的服务器都可以找到另一台服务器以达到“大多数”。不过,这样就需要将服务器分散到 3 个地方。

如何进行选举

当一个从节点无法与主节点连通时,它就会联系并请求其他的副本集成员将自己选举为主节点。其他成员会做几项健全性检查:它们能否连接到一个主节点,而这个主节点是发起选举的节点无法连接到的?这个发起选举的从节点是否有最新数据?有没有其他更高优先级的成员可以被选举为主节点

MongoDB 在其 3.2 版本中引入了第 1 版复制协议。第 1 版协议基于斯坦福大学的 Diego Ongaro 和 John Ousterhout 开发的 RAFT 共识协议。这是一个类 RAFT 的协议,并且包含了一些特定于 MongoDB 的副本集概念,比如仲裁节点、优级、非选举成员、写入关注点(write concern)等。第 1 版协议提供了很多新特性的基础,比如更短的故障转移时间,以及大大减少了检测主节点失效的时间。它还通过使用 term ID 来防止重复投票。

副本集成员相互间每隔两秒发送一次心跳(heartbeat,也就是 ping)。如果某个成员在 10秒内没有反馈心跳,则其他成员会将该不良成员标记为无法访问。选举算法将尽“最大努力”尝试让具有最高优先权的从节点发起选举。成员优先权会影响选举的时机和结果。优先级高的从节点要比优先级低的从节点更快地发起选举,而且也更有可能成为主节点。然而,低优先级的从节点也可能短暂地被选为主节点,即使还存在一个可用的高优先级从节点。副本集成员会继续发起选举直到可用的最高优先级成员被选为主节点。

就所有能连接到的成员,被选为主节点的成员必须拥有最新的复制数据。严格地说,所有的操作都必须比任何一个成员的操作都要高,因此所有的操作都必须比任何一个成员的操作都要晚。

成员配置选项

优先级

优先级用于表示一个成员“渴望”成为主节点的程度。它的取值范围是 0 到 100,默认是 1。将 "priority" 设置为 0 有特殊含义:优先级为 0 的成员永远不能成为主节点。这样的成员称为被动(passive)成员。

拥有最高优先级的成员总是会被选举为主节点(只要它能连接到副本集中的大多数成员,并且拥有最新的数据)。例如,要在副本集中添加一个优先级为 1.5 的成员:

rs.add({"host" : "server-4:27017", "priority" : 1.5})

假设其他成员的优先级都是 1,只要 server-4 拥有最新的数据,当前的主节点就会自动退位,并且 server-4 会被选举为新的主节点。如果 server-4 由于某些原因未能拥有最新的数
据,那么当前主节点就会保持不变。设置优先级并不会导致副本集中无法选举出主节点,也不会使在数据同步中落后的成员成为主节点(一直到它的数据更新到最新)。

"priority" 的绝对值只与它是否大于或小于副本集中的其他优先级有关:优先级为 100、1和 1 的一组成员与优先级为 2、1 和 1 的另一组成员行为方式相同。

隐藏成员

客户端不会向隐藏成员发送请求,隐藏成员也不会优先作为副本集的数据源(尽管当其他复制源不可用时隐藏成员也会被使用)。因此,很多人会将性能较弱的服务器或者备份服务器隐藏起来。

选举仲裁者

同步

复制是指在多台服务器上保持相同的数据副本。MongoDB 实现此功能的方式是保存操作日志(oplog),其中包含了主节点执行的每一次写操作。oplog 是存在于主节点 local 数据库中的一个固定集合。从节点通过查询此集合以获取需要复制的操作。

每个从节点都维护着自己的 oplog,用来记录它从主节点复制的每个操作。这使得每个成员都可以被用作其他成员的同步源,如图 11-1 所示。从节点从同步源中获取操作,将其应用到自己的数据集上,然后再写入 oplog 中。如果应用某个操作失败(只有在基础数据已损坏或数据与主节点不一致时才会发生这种情况),则从节点会停止从当前数据源复制数据。

如果一个从节点由于某种原因而停止运行,那么当它重新启动后,就会从 oplog 中的最后一个操作开始同步。由于这些操作是先应用到数据上然后再写入 oplog,因此从节点可能会重复已经应用到其数据上的操作。MongoDB 在设计时就考虑到了这种情况:将 oplog 中的同一个操作执行多次与只执行一次效果是一样的。oplog 中的每个操作都是幂等的。也就是说,无论对目标数据集应用一次还是多次,oplog 操作都会产生相同的结果。

img

oplog 中按顺序保存着所有执行过的写操作。每个成员都维护了一份自己的 oplog,它们应该和主节点的 oplog 完全一致(可能会有一些延迟)

在大多数情况下,默认的 oplog 大小就足够了。如果预测副本集的工作负载属于以下模式之一,那么你可能会希望创建一个大于默认值的 oplog。相反,如果应用程序主要执行读操作而执行很少的写操作,那么一个较小的 oplog 就足够了。以下这些工作负载可能会需要更大的 oplog。

初始化同步

MongoDB 在执行初始化同步时,会将所有数据从副本集中的一个成员复制到另一个成员中。当一个副本集成员启动时,它会检查自身的有效状态,以确定是否可以开始从其他成员中同步数据。如果状态有效,它就会尝试从该副本集的另一个成员中复制数据的完整副本。

然而,更推荐从备份中进行恢复。从备份中恢复通常比通过mongod 复制所有的数据要快。

克隆可能会破坏同步源的工作集。在许多情况下,某些数据的子集经常会被访问,因而这部分数据总是存在于内存中(因为操作系统经常对其进行访问)。执行初始化同步会强制此成员将其所有数据分页加载到内存中,从而“驱逐”那些经常使用的数据。当那些通常由 RAM 中的数据进行处理的请求突然被迫转到磁盘时,可能此成员的速度会显著降低。不过,对于那些小型数据集和性能较好的服务器,初始化同步是一个简单易用的选择。

复制

MongoDB 执行的第二种同步是复制。从节点成员在初始化同步之后会持续复制数据。它们从同步源复制 oplog,并在一个异步进程中应用这些操作。从节点可以根据需要自动更改同步源,以应对 ping 时间及其他成员复制状态的变化。有一些规则可以控制给定节点从哪些成员进行同步。例如,拥有投票权的副本集成员不能从没有投票权的成员那里同步数据,从节点不能从延迟成员和隐藏成员那里同步数据。

处理过时数据

如果某个从节点远远落后于同步源当前的操作,那么这个从节点就是过时的。过时的从节点无法赶上同步源,因为同步源上的操作过于领先了:如果继续同步,从节点就需要跳过一些操作。这种情况可能发生在以下场景中:从节点服务器停止运行,写操作超过了自身处理能力,或者忙于处理过多的读请求。

当一个从节点过期时,它将依次尝试从副本集中的每个成员进行复制,看看是否有成员拥有更长的oplog以继续进行同步。如果没有一个成员拥有足够长的oplog,那么该成员上的复制将停止,并且需要重新进行完全同步或从最近的备份中恢复。

为了避免出现不同步的从节点,让主节点拥有一个比较大的oplog以保存足够多的操作日志是很重要的。一个更大的oplog会占用更多的磁盘空间,但通常这是一个很好的折中,因为磁盘空间一般来说比较便宜,而且实际中使用的oplog只有一小部分,所以不会占用太多的RAM。根据经验,oplog应该可以覆盖两到三天的正常操作(复制窗口)

心跳

每个成员需要知道其他成员的状态:谁是主节点?谁可以作为同步源?谁停止运行了?为了维护副本集的最新视图,所有成员每隔两秒会向副本集的其他成员发送一个心跳请求。

心跳请求用于检查每个成员的状态。心跳的一个最重要的功能是让主节点知道自己是否满足副本集“大多数”的条件。如果主节点不再得到“大多数”节点的支持,它就会降级,成为一个从节点

成员状态

成员还会通过心跳来传达自己的状态。前面已经讨论了两种状态:主节点和从节点。还有以下一些正常状态。

  • STARTUP
    这是成员在第一次启动时的状态,这时MongoDB正在尝试加载它的副本集配置。一旦配置被加载,它就转换到STARTUP2状态。
  • STARTUP2
    初始同步过程时会持续处于这个状态,通常只需几秒。成员会创造出几个线程来处理复制和选举,然后转换到下一个状态:RECOVERING。
  • RECOVERING
    此状态表明成员运行正常,但不能处理读请求。这可能是因为以下几种情况。在启动时,成员必须做一些检查以确保自己处于有效的状态,之后才能接受读请求。因此,在启动过程中,所有的成员在成为从节点之前都需要经历短暂的RECOVERING状态。在处理一些耗时操作(比如压缩或者相应replSetMaintenance命令)时,成员也可能进入RECOVERING状态。


    如果一个成员远远落后于其他成员而无法赶上时,也会进入RECOVERING状态。通常来说,这是需要进行重新同步的无效状态。这时,该成员不会进入错误状态,因为它希望找到一个拥有足够长oplog的成员,从而引导自己回到非过时状态。
  • ARBITER
    这是仲裁者节点独有的一个特殊状态,并且其在正常运行期间应该始终处于这个状态。

以下一些状态表明系统存在问题。

  • DOWN
    如果一个成员被正常启动,但后来变为不可访问,那么就会进入这种状态。注意,被报告为DOWN状态的成员实际上可能仍在运行,只是由于网络问题而无法访问。
  • UNKNOWN
    如果一个成员从未能访问到另一个成员,那么就不知道它处于什么状态,因此会将其报告为UNKNOWN。这通常表示这个未知成员已停止运行或两个成员之间存在网络问题。
  • REMOVED
    这个状态表示此成员已被从副本集中移除。如果移除的成员被添加回副本集,它就会转换回“正常”的状态。
  • ROLLBACK
    当成员正在回滚数据时会处于此状态,如11.4节所述。在回滚过程结束时,服务器会转换回RECOVERING状态,然后成为从节点

选举

当一个成员无法访问到主节点(而且本身有资格成为主节点)时,便会申请选举。申请选举的成员会向其所能访问到的所有成员发出通知。如果这个成员不适合作为主节点,那么其他成员会知道原因:可能这个成员的数据落后于副本集,或者已经有一个主节点在申请选举,而那个失败的成员无法访问到此节点。在这些情况下,其他成员将投票反对该成员的申请。

假如没有理由反对,其他成员就会为申请当选的成员投赞成票。如果申请选举的成员从副本集中获得了大多数选票,选举就成功了,该成员将过渡到PRIMARY状态。如果没有获得大多数选票,那么它会继续处于从节点状态,以后可能会试图再次成为主节点。主节点会一直处于主节点状态,直到不能满足“大多数”的要求、停止运行、降级,或者副本集被重新配置为止。

如果网络状态正常并且大多数服务器正常运行,那么选举过程应该是很快的。如果主节点不可用,那么两秒(由于前面提到过心跳的间隔是两秒)之内就会有成员发现这个问题,它会立即开始一个选举,而这个过程应该只需要几毫秒。然而,实际情况不会这么理想:选举可能由网络问题或服务器过载导致的响应过慢而触发。在这些情况下,选举过程可能需要更多的时间,甚至会花几分钟。

回滚

如果主节点执行一个写操作之后停止了运行,而从节点还没来得及复制此操作,那么新选举出的主节点可能会丢失这个写操作。假设有两个数据中心,其中一个数据中心拥有一个主节点和一个从节点,另一个数据中心拥有3个从节点

img

假设在两个数据中心之间发生了网络分区故障,其中第一个数据中心最后的操作是126,但是该操作还没有复制到另一个数据中心的服务器。

img

另一个数据中心的服务器仍然可以满足副本集的“大多数”要求(一共5台服务器,3台即可满足要求)。因此,其中一台可能会被选为主节点。这个新的主节点会开始进行自己的写入请求处理

img

当网络恢复后,第一个数据中心的服务器会寻找126号操作以开始与其他服务器的同步,但无法找到这个操作。当这种情况发生时,A和B将开始一个名为回滚(rollback)的过程。回滚用于撤销在故障转移前未复制的操作。具有126号操作的服务器会在另一个数据中心服务器的oplog中寻找公共的操作点。它们会发现125号操作是相互匹配的最后一个操作。

img

A显然是在复制126至128号操作之前崩溃的,所以
这些操作不在B上,而B有更新的操作。A在恢复同步之前必须回滚这3个操作。

这时,服务器会遍历这些操作,并将受这些操作影响的每个文档写入一个.bson文件,保存在数据目录下的rollback目录中。因此,如果126是一个更新操作,那么被126号操作所更新的文档会被写入<collectionName>.bson文件中。然后会从当前主节点的版本中复制这个文档


  1. RAFT 是一种共识算法,它被分解成了相对独立的子问题。共识是指多台服务器或进程在一些值上达成一致的过程。RAFT 确保了一致性,使得同一序列的命令产生相同序列的结果,并在所部署的各个成员中达到相同序列的状态。 ↩︎

posted @   每天提醒自己要学习  阅读(61)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· 单线程的Redis速度为什么快?
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
点击右上角即可分享
微信分享提示