分布式系统最本质的功能是通过提供数据冗余存储能力,提高系统的持久度和可用性,这也是分布式NOSQL系统最复杂的一块,笔者准备从整体架构、选举、数据同步、故障处理等几个维度逐步揭示MongoDB副本集功能的原理。
上次介绍了MongoDB复本集的选举机制,今天来介绍一下复本集的数据同步机制。MongoDB复本集基于Raft一致性协议,我们先来看一下Raft的数据同步机制。
Raft数据同步机制
Raft一致性算法基于复制状态机,复制状态机是通过复制日志来实现的。每一台服务器保存着一份日志,日志中包含一系列的命令,状态机会按顺序执行这些命令。
因为每一台计算机的状态机都是确定的,所以每个状态机的状态都是相同的,执行的命令是相同的,最后的执行结果也就是一样的了。

应用于实际系统的一致性算法一般有以下特性:
高可用性,只要集群中的大多数节点都能运行,可以互相通信并且可以和客户端通信,这个集群就可用。因此,一般来说,一个拥有3台节点的集群可以容忍其中1台节点的崩溃。
不依赖时序保证一致性,时钟错误和极端情况下消息延迟的情况下才会引起可用性问题。
通常情况下,大多数节点作出响应则认为完成,一少部分慢的机器不会影响系统的整体性能。
MongoDB复本集数据同步机制
MongoDB副本集数据同步分为两种:初始同步 和 稳定同步
初始同步(initial sync)

初始同步发生在一个节点刚加入复本集时,节点首先会在复本集中选择一个同步源,然后会重建本地的oplog,再启动一个oplogFetcher从同步源拉取oplog缓存在本地。
初始化同步的意义是让节点通过该步骤同步数据后,让副本集达到一个一致性状态,以便进入后面的稳定同步。
但是由于在同步过程中同步源可能还在接收业务,数据在不断变化,那么何时能达到所谓的一致性状态?
故MongDB将始同步定义为两个阶段:Clone和application。
在clone阶段,可以理解为一个全量拷贝阶段,通过DatabaseCloner和CollectionCloner读取同步源存量数据。
在application阶段,新加入节点首先向同步源查询当前最新apply的Oplog的OpTime,把这个OpTime作为之前提到的一致性点(minValid),然后开始回放oplog缓存中的操作直到这个minValid。
初始化同步完成后,节点就进入了稳定同步状态。
稳定同步(SteadyStateReplication)

在稳定同步状态,后台会启动三个线程:
OplogFetcher:从同步源拉取oplog缓存在本地。
OplogApplier:从缓存中读取Oplog在本地回放,更新lastAppliyOpTime和lastDurableTime。
OplogReporter:向同步源汇报本节点已经回放的oplog的lastAppliyOpTime和lastDurableTime,同步源收到汇报后会刷新Topology视图。
同步源选择
同步源选择分两个步骤:同步源候选人选择和同步源探测。
节点在选择同步源候选人的时候,首先会考虑用户的replSetSyncFrom命令,优先遵从用户指令。
然后会检测settings.chainingAllowed配置是否开启,如果没有开启,默认选择primary为同步源。否则按照:
查看Topology视图中Primary的最近OpTime,如果有别的节点的最近OpTime在Primary的Optime后面,则排除这个节点作为同步源候选人。
选择Ping时延最小的节点作为同步源候选人。
如果没有节点符合条件,1秒后重新开始选择。
选择了候选人后,就进入同步源探测阶段,确保该同步源可以提供Oplog:
检查同步源是否有oplog,没有则加入黑名单,下次不会选择其作为候选人。
检查同步源最老的oplog是否新于本节点最新的oplog,如果是则加入黑名单,下次不会选择其作为候选人。
如果本节点有之前提到的一致性时间点minValid,那么检查同步源是否也有,没有则加入黑名单,下次不会选择其作为候选人。
讲完数据同步的主要架构和流程,我们再来看看数据同步的载体:Oplog
Oplog结构
ts: 操作发生的时间
h: 记录的唯一ID
v: 版本信息
op: 操作的类型
ns: 操作针对的数据库、表
o2: 更新时的过滤条件
o: 操作针对的文档
举例如下:
{ "ts" : Timestamp(1466585597, 1), "h" : NumberLong("-9079402863358495470"), "v" : 2, "op" : "u", "ns" : "test.user", "o2" : { "_id" : ObjectId("576a51c7e8696f07b2b0b272") }, "o" : { "_id" : ObjectId("576a51c7e8696f07b2b0b272"), "name" : "xxx" } }
oplog在回放时具备幂等性,多次回放同一条oplog不会影响数据结果,保证upsert语义。
总结
MongoDB复本集通过OpLog的同步,保证复本集的数据一致性。通过lastApplyOptime和lastDurableOptime的反馈,实现了在所有节点上的一个一致性视图,这样可以很方便为用户提供不同级别的一致性IO服务:WriteConcern/ReadConcern。

未完待续。。。
.......................... END ..........................

长按二维码关注




