* Z+ a+ b# Y( c! x" y, n# Dfor each member that is healthy: ' \ V$ W. z' \2 K; V if member[state] == PRIMARY/ Q% B0 d, H) Z [- T( A8 B9 P
add to set of possible sync targets. h8 X- Q3 X+ v$ G4 U: k/ w
5 r4 x5 }: I9 @' L4 Z7 `% v if member[lastOpTimeWritten] > our[lastOpTimeWritten]$ `' ]% Z% Z% F4 p( ^2 D: v6 w8 c A
add to set of possible sync targets 7 U! _. m f* L' E- B9 k, b' I3 t$ C
sync target = member with the min ping time from the possible sync targets 0 h8 k5 f# T1 G: Z ! h& y6 g) Q M3 \( o 对于节点是否健康,MongoDB各个版本的判断依据有所不同,但都是为了找到能够正常运行的节点。3 x4 x) k8 S9 o
8 {$ q/ ^+ f5 x! p
我们可以通过运行db.adminCommand({replSetGetStatus:1})命令来查看当前的节点状况,在secondary上运行这个命令的时候,能够看到syncingTo这个字段,这个字段的值就表示secondary节点同步数据的源节点。. }. O4 K# q0 J' l0 @% ~5 }
/ D, y% U, s l3 q链式同步1 k" n% C2 I" v: n" b
6 K9 l7 O4 N5 h1 W- R
前边所说的内容,都是假设有一个primary和一个secondary,这种情况下的同步过程比较简单,但是如果有2个secondary或者更多,那么这个过程就要复杂的多。 ; I" ]* [# \; {8 Y$ J% c/ f8 R9 G" m0 L/ }' W
我们用w:3来说明这个问题。比如S1和S2节点是secondary节点, P节点是primary节点,S1节点从P节点同步数据,S2节点从S1节点同步数据。这样P -> S1 -> S2 之间就形成了一个链。如果我们设定w为3,那么除了primary写入数据,还需要有两个secondaris完成同步,才可以返回成功。那么P节点如何能知道S2节点已经从S1节点同步成功了呢? L- E' b. T) h$ d! F( d: {8 O1 n" o; u9 i) a' [3 @4 j% Z: U% N' d
MongoDB通过oplog同步协议来解决上述的多个节点同步的问题。 6 R# t9 M8 N2 g6 M1 ` ]$ U ) y3 Y& L) ~( [; x* `6 u- d. w, r 当S2从S1同步数据时,S2会给S1发送一个特殊的握手消息,“Hi,我是S2,我要从你这同步数据了,把我也算到w参数里边吧。” ! i" M" f$ r9 G: F $ \, F$ w& {# T, u( z! p 当S1收到这个消息的时候,会说,“我不是primary节点,我可以把你这个计数转到我的同步源中去。”然后S1打开一个到P的新的连接,然后对P说,“这个连接你就当是S2的吧,把S2也算到w的计数中。”这个时候,S1和P之间有两个连接,一个是S1自己的,一个是为S2建立的。 % F! D9 p! [: |1 J( h 8 f# z/ Z0 _" @2 v4 p8 B8 s 当P执行完写操作之后,S1首先会获取到P的oplog,执行完这个写操作之后,会告诉P,我已经执行完了。然后S2从S1获取到最新的oplog,同样执行这个写操作,执行完之后,告诉S1,我已经执行完了。S1在收到S2执行完毕的消息后,就通过S1代替S2建立的和P的连接,告诉P,我是代替S2建立的连接,现在S2也执行完这个写操作了。这个时候,P就知道已经有P、S1和S2都完成了这个写操作,w:3已经满足了,然后返回成功,完成这次操作。5 z% M8 j$ `7 U3 Y3 w# p4 [
* o) N3 M9 {* h
具体三个节点间的连接如下图:$ q( |+ ]' a n: M( r& V' _* f! i