mongodb 正本集
应用复制能够将数据正本保留到多台服务器上,即便一台或多台服务器出错,也能够保障应用程序失常运行和数据安全。
在 mongodb 中,创立一个正本集之后就能够应用复制性能了。正本集是一组服务器,其中有一个主服务器,用于解决客户端申请;还有多个备份服务器,用于保留主服务器的数据正本。如果主服务器解体了,备份服务器会主动将其中一个成员降级为新的主服务器。
客户端不能在备份节点执行写操作
默认状况下,客户端不能从备份节点读取数据。在备份节点上显式地执行 setSlaveOK 之后,客户端就能够从备份节点读取数据了。
正本集中很重要的一个概念是 大多数 :抉择主节点时须要由大多数决定,主节点只有在失去大多数反对时能力持续作为主节点。写操作被复制到大多数成员时这个写操作就是平安的。这里的 大多数 被定义为 正本集中一半以上的成员。
假如一个蕴含 5 个成员的正本集,其中 3 个成员不可用,依然有 2 个能够失常工作,残余的 2 个成员曾经无奈达到正本集 大多数 的要求(大多是为 3 个),所以它们无奈选举主节点。如果这 2 个成员中有一个是主节点,当它留神到它无奈失去 大多数 成员反对时,就会从主节点上退位。几秒钟之后,这个正本集中会蕴含 2 个备份节点和 3 个不可达节点。
选举机制
当一个备份节点无奈与主节点连通时,它就会分割其余的正本集成员将本人选举为主节点。其余成员会做几项感性查看:
- 其余成员本身是否能和主节点连通
- 心愿被选举为主节点的备份节点数据是否最新
- 有没有其余更高优先级的成员被选举为主节点
如果要求被选举为主节点的成员可能失去正本集中 大多数 成员的投票,它就会成为主节点。如果大多数成员中只有一个否决了本次选举,选举就会被勾销。如果成员发现任何起因,表明以后心愿成为主节点的成员不应该成为主节点,那么它就会否决此次的选举。
一张否决票相当于 10000 张赞成票,即值为 -10000
复制操作是严格按工夫排序的,所以候选人的最初一条操作要比它能连通的其余所有成员更晚(或者与其余成员相等)。
成员配置选项
仲裁者
很多人的应用程序使用量比拟小,并不想保留三份数据正本。两份正本曾经足够了,保留三份正本的话纯正是节约人力,物力,财力。对于这种部署,mongodb 反对一种非凡类型的成员,成为仲裁者。仲裁者的惟一作用就是参加选举。仲裁者并不保留数据,也不会为客户端提供服务,它只是为了帮忙具备两个成员的正本集可能满足 大多数 这个条件。
因为仲裁者并不需要履行传统 mongod 服务器的责任,所以能够将仲裁者作为轻量级过程,运行在配置比拟差的服务器上。如果能够,能够将仲裁者放在独自的故障域中,与其余成员离开。这样他就能够以内部视角来对待正本集中的成员了。
能够应用 rs.addArb("$ip:$port")
或在成员配置中指定 arbiterOnlt 选项rs.add({"_id":$id, "host":"$ip:$port", "arbiterOnly":true})
。
成员一旦以仲裁者的身份增加到正本集中,它就永远只能是仲裁者,无奈将仲裁者重新配置为非仲裁者,反之亦然。
应用仲裁者的另一个益处是,如果节点领有的节点数是偶数,仲裁者能够投出决定输赢的要害一票。
优先级
优先级用于示意一个成员渴望成为主节点的水平,取值范畴是 1 -100,默认是 1。将优先级设为 0 有非凡含意:优先级为 0 的成员永远不可能成为主节点,这样的成员被称为被动成员。领有最高优先级的成员会优先选举为主节点(只有它可能失去汇合中大多数成员的赞成票,并且数据是最新的)
暗藏成员
客户端不会向暗藏成员发送申请,暗藏成员也不会作为复制源。因而,很多人会将不够弱小的服务器或备份服务器暗藏起来。只有优先级为 0 的成员能力被暗藏 。即设置priority=0
和hidden=true
提早备份节点
数据可能会因为人为谬误而蒙受毁灭性的毁坏,为了避免这类问题,能够应用 slaveDelay 设置一个提早的备份节点。
提早备份节点会比主节点提早指定的工夫(秒),这是无意为之的。这样,如果有人不小心捣毁了你的主汇合,还能够将数据从先前的备份中恢复过来。slaveDelay 要求成员的优先级是 0 ,如果利用会将读申请路由到备份节点,应该将提早备份节点暗藏掉,免得读申请被路由到提早备份节点。
不创立索引的成员
有时备份节点并不需要与主节点领有雷同的索引,甚至能够没有索引。如果某个备份节点的用处仅仅是解决数据备份或者离线的批量工作,那么你可能心愿在它的成员配置中指定 "buildIndexes":false
。这个选项能够阻止备份节点创立索引。这是一个永恒选项,这个成员将永远无奈复原为能够创立索引的成员。 要求成员的优先级为 0 。
同步的过程
mongodb 的复制性能是应用操作日志 oplog 实现的,操作日志蕴含了主节点的每一次写操作。oplog 是主节点的 local 数据库中的一个固定汇合。备份节点通过查问这个汇合就能够晓得须要进行复制的操作。
每个备份节点都保护着本人的 oplog,记录着每一次从主节点复制数据的操作。这样,每个成员都能够作为同步源提供给其余成员应用。
备份节点从以后应用的同步源中获取须要执行的操作,而后在本人的数据集上执行这些操作,最初再将这些操作写入本人的 oplog。如果某个备份节点挂掉了,当它重新启动后,就会主动从 oplog 中最初一个操作开始进行同步。因为复制操作的过程是先复制数据再写入 oplog,所以备份节点可能会在曾经同步过的数据上再次执行复制操作。mongodb 在设计之初就思考到了这种状况:将 oplog 的同一个操作执行屡次与只执行一次的成果是一样的。
因为 oplog 的大小是固定的,它只能保留特定数量的操作日志。如果单个操作会影响多个文档,例如db.coll.remove()
,删除了 10000 个文档,那么 oplog 中就会有 10000 条操作日志,每条日志对应一个被删除的文档。如果执行大量的批量操作,oplog 很快就会被填满。
抉择同步源
MongoDB 默认是采取级联复制的架构,就是默认不肯定抉择主库作为本人的同步源,如果不想让其进行级联复制,能够通过 chainingAllowed 参数来进行管制。在级联复制的状况下,你也能够通过 replSetSyncFrom 命令来指定你想复制的同步源。所以这里说的同步源其实绝对于从库来说就是它的主库。那么同步源的选取流程是怎么的呢?
MongoDB 从库会在正本集其余节点通过以下条件筛选合乎本人的同步源。
- 如果设置了 chainingAllowed 为 false,那么只能选取主库为同步源
- 找到与本人 ping 工夫最小的并且数据比本人新的节点(在正本集初始化的时候,或者新节点退出正本集的时候,新节点对正本集的其余节点至多 ping 两次)
- 该同步源与主库最新 optime 做比照,如果提早主库超过 30s,则不抉择该同步源。
在第一次的过滤中,首先会淘汰比本人数据还旧的节点。如果第一次没有,那么第二次须要算上这些节点,避免最初没有节点能够做为同步源了。最初确认该节点是否被禁止参加选举,如果是则跳过该节点。通过上述筛选最初过滤出来的节点作为新的同步源。
其实 MongoDB 同步源在除了在 Initial Sync 和增量复制 的时候选定之后呢,并不是始终是稳固的,它可能在以下状况下进行变更同步源:
- ping 不通本人的同步源
- 本人的同步源角色发生变化
- 本人的同步源与正本集任意一个节点提早超过 30s
删除除 local 以外的所有数据库
全量同步
- 在创立的汇合的时候同时创立了索引(与主库一样),在 MongoDB 3.4 版本之前只创立 _id 索引,其余索引期待数据 copy 实现之后进行创立。
- 在创立汇合和拷贝数据的同时,也将 oplog 拷贝到本地 local 数据库中,等到数据拷贝实现之后,开始利用本地 oplog 数据。
克隆数据是耗时操作,如果克隆实现后,新成员数据同步速度赶不上同步源的变动速度,同步源可能会将新成员须要复制的某些数据 oplog 笼罩掉。
备份节点远远落后于同步源以后的操作,那么这个备份节点就是古老的。
当一个备份节点古老之后,它会查看正本集中的其余成员,如果某个成员的 oplog 足够详尽,能够用于解决那些落下的操作,就从这个成员处进行同步。如果任何一个成员的 oplog 都没有参考价值,那么这个成员上的复制操作就会停止,这个成员须要从新进行全量同步。
增量同步
- Sencondary 初始化同步实现之后,开始增量复制,通过 produce 线程在 Primary oplog.rs 汇合上建设 cursor,并且实时申请获取数据。
- Primary 返回 oplog 数据给 Secondary。
- Sencondary 读取到 Primary 发送过去的 oplog,将其写入到队列中。
-
Sencondary 的同步线程会通过 tryPopAndWaitForMore 办法始终生产队列,当每次达到肯定的条件之后,条件如下:
- 总数据大于 100MB
- 曾经取到局部数据但没到 100MB,然而目前队列没数据了,这个时候会阻塞期待一秒,如果还没有数据则本次取数据实现。
上述两个条件满足一个之后,就会将数据给 prefetchOps 办法解决,prefetchOps 办法次要将数据以 database 级别切分,便于前面多线程写入到数据库中。如果采纳的 WiredTiger 引擎,那这里是以 Docment ID 进行切分。
- 最终将划分好的数据以多线程的形式批量写入到数据库中(在从库批量写入数据的时候 MongoDB 会阻塞所有的读)。
- 而后再将 Queue 中的 Oplog 数据写入到 Sencondary 中的 oplog.rs 汇合中。
心跳
每个成员须要晓得其余成员的状态:哪个是主节点?哪个能够作为同步源?哪个挂掉了?为了保护汇合的最新视图,每个成员每隔 2 秒就会向其余成员发送一个心跳申请。心跳申请的信息量很小,用于查看每个成员的状态。
心跳最重要的性能之一就是让主节点晓得本人是否满足汇合大多数的条件。如果主节点不再失去大多数服务器的反对,它就会退位,变成备份节点。
成员状态
各个成员会通过心跳将本人以后的状态通知其余成员。
- STARTUP
成员刚启动时处于这个状态,在这个状态下,mongodb 会尝试加载成员的正本集配置。配置加载胜利后,就进入 STARTUP2 状态。
- STARTUP2
整个初始化同步过程都处于这个状态。当初始化同步实现后,进入 RECOVERING 状态。
- RECOVERING
这个状态表明成员运行失常,然而临时还不能解决读取申请。在解决十分耗时的操作时,成员也可能进入 RECOVERING 状态,比方压缩或 replSetMaintenance
命令。当一个成员和其余成员脱节时,也会进入这个状态,通常来说,这时这个成员处于有效状态,须要从新同步。然而,成员这时并没有进入谬误状态,因为它冀望一个领有足够详尽 oplog 的成员,而后持续同步 oplog,而后回到失常状态。
- ARBITER
仲裁者状态。
- DOWN
不可达的成员会被报告为 DOWN 状态。它有可能依然是运行状态,只是网络不可达。
- UNKNOWN
别的成员临时不晓得该成员的状态,会被报告为该状态
- REMOVED
成员被移除正本集时的状态
- ROLLBACK
正在进行数据回滚的状态,回滚实现后会转换为 RECOVERING 状态
- FATAL
一个成员产生了不可挽回的谬误,也不再尝试恢复正常。应该查看具体日志查明为何该成员处于 FATAL 状态。通常应该重启服务器,进行从新同步或者从备份中复原
- PRIMARY
主节点状态
- SECONDARY
备份节点状态
选举
当一个节点无奈达到主节点时,它就会申请被选举为主节点。心愿被选举为主节点的成员会向它能达到的所有成员发告诉。如果这个成员不合乎候选人的要求:
- 这个成员的数据落后于正本集
- 有一个运行中的主节点(那个力求被选举为主节点的成员无奈达到这个主节点)
在这些状况下,其余成员不会容许进行选举。
退出没有拥护的理由,其余成员就会对这个成员进行选举投票。如果这个成员失去正本集中大多数成员的赞成票,它就会选举胜利,会转换到主节点状态。如果达不到大多数成员的要求,就会选举失败,依然处于备份节点的状态。之后还能够再次申请被选举为主节点。主节点若不再满足大多数节点的要求,会退位。
心跳超时工夫为 20 秒,若选举达成平局,每个成员须要期待 30 秒能力开始下一次选举。
回滚
咱们晓得在产生切换的时候是有可能造成数据失落的,次要是因为主库宕机,然而新写入的数据还没有来得及同步到从库中,这个时候就会产生数据失落的状况。
那针对这种状况,MongoDB 减少了回滚的机制。在主库复原后重新加入到复制集中,这个时候老主库会与同步源比照 oplog 信息,这时候分为以下两种状况:
- 在同步源中没有找到比老主库新的 oplog 信息。
- 同步源最新一条 oplog 信息跟老主库的 optime 和 oplog 的 hash 内容不同。
针对上述两种状况 MongoDB 会进行回滚,回滚的过程就是逆向比照 oplog 的信息,直到在老主库和同步源中找到对应的 oplog,而后将这期间老主库的 oplog 全副记录到 rollback 目录里的文件中,并将这期间的 oplog 撤销。
之后能够将这些被回滚的操作利用到以后的主节点。
以下状况会终止回滚:
- 比照老主库的 optime 和同步源的 optime,如果超过了 30 分钟,那么放弃回滚。
- 在回滚的过程中,如果发现单条 oplog 超过 512M,则放弃回滚。
- 如果有 dropDatabase 操作,则放弃回滚。
- 最终生成的回滚记录超过 300M,也会放弃回滚。
这种状况最常见的起因是备份节点远远落后于主节点,而这时主节点挂了。如果其中一个备份节点成为主节点,这个主节点与旧的主节点相比,短少很多操作。为了保障成员不会在备份中失败,最好的形式是放弃备份节点的数据尽可能的最新。
参考文章
https://www.infoq.cn/article/…
《mongodb 权威指南》