ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Mongo副本集的组成

2021-10-09 12:32:08  阅读:236  来源: 互联网

标签:副本 Mongo replSet 成员 30 组成 oplog 节点 rsSync


  本章介绍副本集的各个部分是如何组织在一起的,包括:

    • 副本集成员如何复制新数据;
    • 如何让新成员开始工作;
    • 选举机制;
    • 可能的服务器和网络故障。

 

1.同步

  复制用于在多台服务器之间备份数据。MongoDB的复制功能是使用操作日志oplog 实现的,操作日志包含了主节点的每一次写操作。oplog是主节点的local数据库中的一个固定集合。备份节点通过査询这个集合就可以知道需要进行复制的操作。

  每个备份节点都维护着自己的oplog,记录着每一次从主节点复制数据的操作。这样,每个成员都可以作为同步源提供给其他成员使用,如图10-1所示。备份节点从当前使用的同步源中获取需要执行的操作,然后在自己的数据集上执行这些操作,最后再将这些操作写入自己的oplog。如果遇到某个操作失败的情况(只有当同步源的数据损坏或者数据与主节点不一致时才可能发生),那么备份节点就会停止从当前的同步源复制数据。

  如果某个备份节点由于某些原因挂掉了,当它重新启动之后,就会自动从oplog中最后一个操作开始进行同步。由于复制操作的过程是先复制数据再写入oplog,所以,备份节点可能会在已经同步过的数据上再次执行复制操作。MongoDB在设计之初就考虑到了这种情况:将oplog中的同一个操作执行多次,与只执行一次的效果是一样的。

  由于oplog大小是固定的,它只能保存特定数量的操作日志。通常,oplog使用空间的增长速度与系统处理写请求的速率近乎相同:如果主节点上每分钟处理了 1 KB 的写入请求,那么oplog很可能也会在一分钟内写入1KB条操作日志。但是,有一些例外情况:如果单次请求能够影响到多个文档(比如删除多个文档或者是多文档更新),oplog中就会出现多条操作日志。如果单个操作会影响多个文档,那么每个受影响的文档都会对应oplog中的一条日志。因此,如果执行db.coll.remove()删除了 1 000 000个文档,那么oplog中就会有1 000 000条操作日志,每条日志对应一个被删除的文档。如果执行大量的批量操作,oplog很快就会被填满。

 

1.1 初始化同步

  副本集中的成员启动之后,就会检査自身状态,确定是否可以从某个成员那里进行同步。如果不行的话,它会尝试从副本的另一个成员那里进行完整的数据复制。这个过程就是初始化同步(initial syncing),包括几个步骤,可以从mongod的日志中看到。

  (1)首先,这个成员会做一些记录前的准备工作:选择一个成员作为同步源,在local.me中为自己创建一个标识符,删除所有已存在的数据库,以一个全新的状态开始进行同步:

Mon Jan    30    11:09:18    [rsSync]    replSet initial sync pending
Mon Jan    30    11:09:18    [rsSync]    replSet syncing to: server-1:27017
Mon Jan    30    11:09:18    [rsSync]    build index local.me { id: 1 }
Mon Jan    30    11:09:18    [rsSync]    build index done 0 records 0 secs
Mon Jan    30    11:09:18    [rsSync]    replSet initial sync drop all databases
Mon Jan    30    11:09:18    [rsSync]    dropAUDatabasesExceptLocal 1

  注意,在这个过程中,所有现有的数据都会被删除。应该只在不需要保留现有数据的情况下做初始化同步(或者将数据移到其他地方),因为mongod会首先将现有数据删除。

  (2) 然后是克隆(cloning),就是将同步源的所有记录全部复制到本地。这通常是整个过程中最耗时的部分:

Mon Jan  30 11:09:18    [rsSync]    replSet initial sync clone all databases
Mon Jan  30 11:09:18    [rsSync]    replSet initial sync cloning db: db1
Mon Jan  30 11:09:18 [fileAllocator] allocating new datafile /data/db/db1.ns, filling with zeroes…

  (3) 然后就进入oplog同步的第一步,克隆过程中的所有操作都会被记录到oplog 中。如果有文档在克隆过程中被移动了,就可能会被遗漏,导致没有被克隆,对于这样的文档,可能需要重新进行克隆:

Mon Jan 30 15:38:36 [rsSync] oplog sync 1 of 3
Mon Jan 30 15:38:36 [rsBackgroundSync] replSet syncing to: server-1:27017 
Mon Jan 30 15:38:37 [ rsSyncNotifier] replset setting oplog notifier to server-1:27017
Mon Jan 30 15:38:37 [repl writer worker 2] replication update of non-mod failed:
        { ts: Timestamp 1352215827000|17, h: -5618036261007523082, v: 2, op: "u", ns: "db1.someColl", o2: { _id: ObjectId('50992a2a78522Gle750012b71) }, o: { $set: { count.0: 2, count.1: 0 } } }
Mon Jan 30 15:38:37 [repl writer worker 2] replication info adding missing object
Mon Jan 30 15:38:37 [repl writer worker 2] replication missing object not found on source, presumably deleted later in oplog

  上面是一个比较粗略的日志,显示了有文档需要重新克隆的情况。在克隆过程中也可能不会遗漏文档,这取决于流量等级和同步源上的操作类型。

  (4) 接下来是oplog同步过程的第二步,用于将第一个oplog同步中的操作记录下来。

Mon Jan 30 15:39:41 [rsSync] oplog sync 2 of 3

  这个过程比较简单,也没有太多的输出。只有在没有东西需要克隆时,这个过程才会与第一个不同。

  (5) 到目前为止,本地的数据应该与主节点在某个时间点的数据集完全一致了,可以开始创建索引了。如果集合比较大,或者要创建的索引比较多,这个过程会很耗时间:

Mon Jan    30    15:39:43    [rsSync]    replSet initial sync building indexes
Mon Jan    30    15:39:43    [rsSync]    replSet initial sync cloning indexes for :    db1
Mon Jan    30    15:39:43    [rsSync]    build index db.allObjects { someColl: 1 }
Mon Jan    30    15:39:44    [rsSync]    build index done, scanned 209844 total records.
1.96 secs

  (6) 如果当前节点的数据仍然远远落后于同步源,那么oplog同步过程的最后一步就是将创建索引期间的所有操作全部同步过来,防止该成员成为备份节点。

Tue Nov 6 16:05:59 [rsSync] oplog sync 3 of 3

  (7) 现在,当前成员已经完成了初始化同步,切换到普通同步状态,这时当前成员就可以成为备份节点了 :

Mon Jan 30 16:07:52 [rsSync] replSet initial sync done
Mon Jan 30 16:07:52 [rsSync] replSet syncing to: server-1:27017
Mon Jan 30 16:07:52 [rsSync] replSet SECONDARY

  如果想跟踪初始化同步过程,最好的方式就是查看服务器日志。

  从操作者的角度来说,初始化同步是非常简单的:使用空的数据目录启动mongod 即可。但是,更多时候可能需要从备份中恢复(其他章节会详细介绍)而不是进行初始化同步。从备份中恢复的速度比使用mongod复制全部数据的速度快得多。

  克隆也可能损坏同步源的工作集(working set)。实际部署之后,可能会有一个频繁使用的数据子集常驻内存(因为操作系统要频繁访问这个子集)。执行初始化同步时,会强制将当前成员的所有数据分页加载到内存中,这会导致需要频繁访问的数据不能常驻内存,所以会导致很多请求变慢,因为原本只要在RAM (内存)中就可以处理的数据要先从磁盘上加载。不过,对于比较小的数据集和性能比较好的服务器,初始化同步仍然是个简单易用的选项。

  初始化同步过程中经常遇到的问题是,第(2)步(克隆)或者第(5)步(创建索引) 耗费了太长的时间。这种情况下,新成员就与同步源的oplog “脱节”:新成员远远落后于同步源,导致新成员的数据同步速度赶不上同步源的变化速度,同步源可能会将新成员需要复制的某些数据覆盖掉。

  这个问题没有有效的解决办法,除非在不太忙时执行初始化同步,或者是从备份中恢复数据。如果新成员与同步源的oplog脱节,初始化同步就无法正常进行。下一节会更深入地介绍。

 

1.2 处理陈旧数据

  如果备份节点远远落后于同步源当前的操作,那么这个备份节点就是陈旧的 (stale)。陈旧的备份节点无法跟上同步源的节奏,因为同步源上的操作领先太多太多:如果要继续进行同步,备份节点需要跳过一些操作。如果从备份节点曾经停机过,写入量超过了自身处理能力,或者是有太多的读请求,这些情况都可能导致备份节点陈旧。

  当一个备份节点陈旧之后,它会査看副本集中的其他成员,如果某个成员的oplog 足够详尽,可以用于处理那些落下的操作,就从这个成员处进行同步。如果任何一个成员的oplog都没有参考价值,那么这个成员上的复制操作就会中止,这个成员需要重新进行完全同步(或者是从最近的备份中恢复)。

  为了避免陈旧备份节点的出现,让主节点使用比较大的oplog保存足够多的操作日志是很重要的。大的oplog会占用更多的磁盘空间。通常来说,这是一个比较好的折衷选择,因为磁盘会越来越便宜,而且实际中使用的oplog只有一小部分,因此oplog不占用太多RAM。关于oplog空间占用的更多信息,12.4.6节会详细介绍。

 

2.心跳

  每个成员都需要知道其他成员的状态:哪个是主节点?哪个可以作为同步源?哪个挂掉了?为了维护集合的最新视图,每个成员每隔两秒钟就会向其他成员发送一个心跳请求(heartbeat request)。心跳请求的信息量非常小,用于检査每个成员的状态。

  心跳最重要的功能之一就是让主节点知道自己是否满足集合“大多数”的条件。如果主节点不再得到“大多数”服务器的支持,它就会退位,变成备份节点。

 

2.1 成员状态

  各个成员会通过心跳将自己的当前状态告诉其他成员。我们已经讨论过两种状态了: 主节点和备份节点。还有其他一些常见状态。

  • STARTUP

  成员刚启动时处于这个状态。在这个状态下,MongoDB会尝试加载成员的副本集配置。配置加载成功之后,就进入STARTUP2状态。

  • STARTUP2

  整个初始化同步都处于这个状态,但是如果是在普通成员上,这个状态只会持续几秒钟。在这个状态下,MongoDB会创建几个线程,用于处理复制和选举,然后就会切换到RECOVERING状态。

  • RECOVERING

  这个状态表明成员运转正常,但是暂时还不能处理读取请求。如果有成员处于这个状态,可能会造成轻微的系统过载,以后可能会经常见到。

  启动时,成员需要做一些检查以确保自己处于有效状态,之后才可以处理读取请求。在启动过程中,成为备份节点之前,每个成员都要经历RECOVERING状态。在处理非常耗时的操作时,成员也可能进入RECOVERING状态。,比如压缩或者是响应 replSetMaintenance 命令。

  当一个成员与其他成员脱节时,也会进入RECOVERING状态。通常来说,这时这个成员处于无效状态,需要重新同步。但是,成员这时并没有进入错误状态,因为它期望发现一个拥有足够详尽oplog的成员,然后继续同步oplog,最后回到正常状态。

  • ARBITER

  在正常的操作中,仲裁者应该始终处于ARBITER状态。

 

  系统出现问题时会处于下面这些状态。 

 

  • DOWN

  如果一个正常运行的成员变得不可达,它就处于DOWN状态。注意,如果有成员被报告为DOWN状态,它有可能仍然处于正常运行状态,不可达的原因可能是网络问题。

  • UNKNOWN

  如果一个成员无法到达其他任何成员,其他成员就无法知道它处于什么状态,会将其报告为UNKNOWN状态。通常,这表明这个未知状态的成员挂掉了,或者是两个成员之间存在网络访问问题。

  • REMOVED

  当成员被移出副本集时,它就处于这个状态。如果被移出的成员又被重新添加到副本集中,它就会回到“正常”状态。

  • ROLLBACK

  如果成员正在进行数据回滚(详见10.4节),它就处于ROLLBACK状态。回滚过 程结束时,服务器会转换为RECOVERING状态,然后成为备份节点。

  • FATAL

  如果一个成员发生了不可挽回的错误,也不再尝试恢复正常的话,它就处于 FATAL状态。应该査看详细日志来查明为何这个成员处于FATAL状态(使用 "replSet FATAL"关键词在日志上执行grep,就可以找到成员进入FATAL状态的时间点)。这时,通常应该重启服务器,进行重新同步或者是从备份中恢复。

 

3.选举

  当一个成员无法到达主节点时,它就会申请被选举为主节点。希望被选举为主节点的成员,会向它能到达的所有成员发送通知。如果这个成员不符合候选人要求,其他成员可能会知道相关原因:这个成员的数据落后于副本集,或者是已经有一个运行中的主节点(那个力求被选举成为主节点的成员无法到达这个主节点)。在这些情况下,其他成员不会允许进行选举。

  假如没有反对的理由,其他成员就会对这个成员进行选举投票。如果这个成员得到副本集中“大多数”赞成票,它就选举成功,会转换到主节点状态。如果达不到“大多数”的要求,那么选举失败,它仍然处于备份节点状态,之后还可以再次申请被选举为主节点。主节点会一直处于主节点状态,除非它由于不再满足“大多数”的要求或者挂了而退位,另外,副本集被重新配置也会导致主节点退位。

  假如网络状况良好,“大多数”服务器也都在正常运行,那么选举过程是很快的。如果主节点不可用,2秒钟(之前讲过,心跳的间隔是2秒)之内就会有成员发现这个问题,然后会立即开始选举,整个选举过程只会花费几毫秒。但是,实际情况可能不会这么理想:网络问题,或者是服务器过载导致响应缓慢,都可能触发选举。在这种情况下,心跳会在最多20秒之后超时。如果选举打成平局,每个成员都需要等待30秒才能开始下一次选举。所以,如果有太多错误发生的话,选举可能会花费几分钟的时间。

 

4.回滚

  根据上一节讲述的选举过程,如果主节点执行了一个写请求之后挂了,但是备份节点还没来得及复制这次操作,那么新选举出来的主节点就会漏掉这次写操作。假如有两个数据中心,其中一个数据中心拥有一个主节点和一个备份节点,另一个数据中心拥有三个备份节点,如图10-2所示。

  如果这两个数据中心之间出现了网络故障,如图10-3所示。其中左边第一个数据中心最后的操作是126,但是126操作还没有被复制到另边的数据中心。

  右边的数据中心仍然满足副本集“大多数”的要求(一共5台服务器,3台即可满足要求)。因此,其中一台服务器会被选举成为新的主节点,这个新的主节点会继续处理后续的写入操作,如图10-4所示。

  网络恢复之后,左边数据中心的服务器就会从其他服务器开始同步126之后的操作, 但是无法找到这个操作。这种情况发生的时候,A和B会进入回滚(rollback)过程。回滚会将失败之前未复制的操作撤消。拥有126操作的服务器会在右边数据中心服务器的oplog中寻找共同的操作点。之后会定位到125操作,这是两个数据中心相匹配的最后一个操作。图10-5显示了 oplog的情况。

  这时,服务器会査看这些没有被复制的操作,将受这些操作影响的文档写入一个.bson文件,保存在数据目录下的rollback目录中。如果126是一个更新操作,服务器会将被126更新的文档写入collectionName.bson文件。然后会从当前主节点中复制这个文档。

  下面是一次典型的回滚过程产生的日志:

Fri Oct    7    06:30:35    [rsSync]    replSet    syncing to:    server-1
Fri Oct    7    06:30:35    [rsSync]    replSet    our last    op    time written: Oct 7 06:30:05:3
Fri Oct 7 06:30:35 [rsSync] replset source's GTE: Oct 7 06:30:31:1 
Fri Oct    7    06:30:35    [rsSync]    replSet    rollback    0
Fri Oct    7    06:30:35    [rsSync]    replSet    ROLLBACK
Fri Oct    7    06:30:35    [rsSync]    replSet    rollback    1
Fri Oct    7    06:30:35    [rsSync]    replSet    rollback    2 findCommonPoint
Fri Oct 7 06:30:35 [rsSync] replSet info rollback our last optime: Oct 7 06:30:05:3
Fri Oct 7 06:30:35 [rsSync] replSet info rollback their last optime: Oct 7 06:30:31:2
Fri Oct 7 06:30:35 [rsSync] replSet info rollback diff in end of log times:-26 seconds
Fri Oct 7 06:30:35 [rsSync] replSet rollback found matching events at Oct 7 06:30:03:4118
Fri Oct    7    06:30:35    [rsSync]    replSet    rollback findcommonpoint    scanned    :    6
Fri Get    7    06:30:35    [rsSync]    replSet    replSet rollback 3 fixup
Fri Oct    7    06:30:35    [rsSync]    replSet    rollback 3.5
Fri Oct    7    06:30:35    [rsSync]    replSet    rollback 4 n:3
Fri Oct    7    06:30:35    [rsSync]    replSet    minvalid=0ct 7 06:30:31    4e8ed4c7:2
Fri    Oct    7    06:30:35    [rsSync]    replSet    rollback    4.6
Fri    Oct    7    06:30:35    [rsSync]    replSet    rollback    4.7
Fri    Oct    7    06:30:35    [rsSync]    replSet    rollback    5 d:6 u:0
Fri    Oct    7    06:30:35    [rsSync]    replSet    rollback    6
Fri    Oct    7    06:30:35    [rsSync]    replSet    rollback    7
Fri    Oct    7    06:30:35    [rsSync]    replSet    rollback    done
Fri    Oct    7    06:30:35    [rsSync]    replSet    RECOVERING
Fri    Oct    7    06:30:36    [rsSync]    replSet    syncing to:    server-1
Fri    Oct    7    06:30:36    [rsSync]    replSet    SECONDARY

  服务器开始从另一个成员进行同步(在本例中是server-1),但是发现无法在同步源中找到自己的最后一次操作。这时,它就会切换到回滚状态("replSet ROLLBACK")进行回滚。

  第2步,服务器在两个oplog中找到一个共同的点,是26秒之前的一个操作。然后服务器就会将最近26秒内执行的操作从oplog中撤销。回滚完成之后,服务器就进入RECOVERING状态开始进行正常同步。

  如果要将被回滚的操作应用到当前主节点,首先使用mongorestore命令将它们加载到一个临时集合:

$ mongorestore --db stage --collection stuff \
>  /data/db/rollback/important.stuff.2012-12-19T18-27-14.0.bson

  现在应该在shell中将这些文档与同步后的集合进行比较。例如,如果有人在被回滚的成员上创建了一个“普通”索引,而当前主节点创建了一个唯一索引,那么就需要确保被回滚的数据中没有重复文档,如果有的话要去除重复。

  如果希望保留staging集合中当前版本的文档,可以将其载入主集合:

>  staging.stuff.find().forEach(function(doc) {
…            prod.stuff.insert(doc);
...})

  对于只允许插入的集合,可以直接将被回滚的文档插入主集合。但是,如果是在集合上执行更新操作,在合并回滚数据时就要非常小心地对待。

  一个经常会被误用的成员配置选项是设置每个成员的投票数量。改变成员的投票数量通常不会得到想要的结果,而且很可能会导致大量的回滚操作(所以上一章的成员属性列表中没有介绍这个选项)。除非做好了定期处理回滚的准备,否则不要改变成员的投票数量。

  第11章会讲述如何阻止回滚。

 

4.1 如果回滚失败

  某些情况下,如果要回滚的内容太多,MongoDB可能承受不了。如果要回滚的数据量大于300 MB,或者要回滚30分钟以上的操作,回滚就会失败。对于回滚失畋的节点,必须要重新同步。

  这种情况最常见的原因是备份节点远远落后于主节点,而这时主节点却挂了。如果其中一个备份节点成为主节点,这个主节点与旧的主节点相比,缺少很多操作。为了保证成员不会在回滚中失败,最好的方式是保持备份节点的数据尽可能最新。

标签:副本,Mongo,replSet,成员,30,组成,oplog,节点,rsSync
来源: https://www.cnblogs.com/lizexiong/p/15385069.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有