如果你这个节点是全新的,没有数据,那么oplog里也没有数据,这时候节点会选择执行一次全量的同步。本文暂时不对全量同步的方法进行描述。
选择同步源节点
Replica
Sets中的节点之间总在同步数据,但是他们不是通过传统的一主多从的方式来同步的。MongoDB的策略是选择一个合适的节点作为数据源。
首先secondary节点会通过ping的时间来确定其它节点与它的距离。时间越长的识为距离越远。然后通过下面方法确定其源节点:
for each member that is healthy:
if member[state] == PRIMARY
add to set of possible sync targets
if member[lastOpTimeWritten] > our[lastOpTimeWritten]
add to set of possible sync targets
sync target = member with the min ping time from the possible sync targets
对于节点是否healthy的判断,各个版本不同,但是其目的都是找出正常运转的节点。在2.0版本中,它的判断还包括了salve
delay这个因素。
你可以通过运行db.adminCommand({replSetGetStatus:1})命令来查看当前的节点状况,在secondary上运行这个命令,你能看到syncingTo这个字段,这个字段的值就是这个secondary的同步源。(其实名字应该是叫syncingFrom,但是由于版本兼容的原因,沿用了这个错误的名字)
链式同步结构
上面对w参数的实现,讲解上比较简单,只讲了w为2的情况,但是当w更大时,由于我们并不是采用一主多从的方式进行同步。所以情况会复杂一些。
比如我们有节点A,为primary节点,然后B节点为secondary节点,它从A节点同步数据,同时又有secondary节点C,它从同是secondary的B节点同步数据。这样A->B->C之间就形成了一个链式的同步结构。如果我们设定w为3,那么A节点如何能知道C节点已经从B节点同步成功了呢?
这是通过oplog同步协议来实现的,我们用通俗的语言来解释一下oplog的同步协议。
当C从B同步数据时,C会在协议中对B说:我要从你这同步数据了,如果写操作有w参数的话,我的同步也算上吧。
然后B会回答说:我不是一个primary节点,我会把你的这个计数转到我的同步源上去。
然后B再对A打开一个新的连接,并且对A说:这个连接你就当成是C的吧,也算一个计数在w里。
这时候在A看来,就有两个连接连到他上面,一个是B,一个是虚拟的C,这两个连接都能报告他说完成了同步操作。
当一个写操作在A上执行后,B首先同步到这个操作的oplog,执行完后会告诉A,我执行完了。然后C同样从B上获取到B的oplog,也执行了这一条写操作,然后他告诉B,我执行完了,B在收到这个响应后,会通过刚才开通的虚拟通道跟A说,我是虚拟的C节点,我也完成写操作了。这时候A就知道,A、B、C三个节点都完成写操作了。w:3的条件满足,然后返回给调用getLastError的客户端,完成这次操作。
具体三个节点间的连接如下图:
C B A
<====>
<====> <---->
B和A之间有两条通道,双线那条是真正的同步连接,单线那条是一个虚拟连接。
新功能展望
上面就是当前的Replica
Sets同步的内部实现,在后续这一块还会进行一些新特性的开发。在2.2版本中,我们会提供replSetSyncFrom命令,让用户可以手动设置一个secondary的同步源。使用方法大概是这样:
> db.adminCommand({replSetSyncFrom:"otherHost:27017"})
敬请期待,thx