7 [9 y: F9 y0 I3 y- i( k 在primary上完成写操作; " }# E( H+ R8 i, a 写操作被记录在primary的oplog中,oplog中包含一个ts字段,记录了写操作发生的时间t; - J, b! p- A8 ^$ i2 R _1 [ 客户端在primary中执行{getLastError:1, w:2}命令,primary完成了写操作,只要再有一个节点完成写操作,就可以满足w:2 了; 9 k) Q C% O% x& G$ |8 z secondary从primary获得oplog,获得上一次操作的记录; " ^( R4 S/ l. \ R( j9 W secondary执行oplog中刚才那一条时间t的操作; ) K! [6 d# W& M% Y# w secondary从primary的oplog中获取时间t之后的log,条件为{ts:{$gt:t}}; ' x! d' ]% w4 k# p; h6 n primary知道了secondary已经成功执行了时间t之前的oplog,因为secondary已经在请求时间t之后的oplog了; 0 D% q" i4 y* Q8 {, M/ W, ~ getLastError知道primary与secondary都完成了这次写操作,于是 w:2 的条件满足了,向客户端返回成功。& k" @, S! o- }; ?1 j0 l
/ ^) S4 u8 R( |$ w; S启动1 W2 j. ^; H* t% I# w0 |# s
" h8 k/ \; `, |2 `& o4 S, i" ]- r7 l# ~ 当在现有的某个replica set中加入一个新节点并启动时,这个新节点会查看自己的local.oplog.rs collection,执行一个叫 lastOpTimeWritten 的命令,查找到它最近的一条被secondary同步过的写操作。 9 V& x8 H1 m2 Q; n( w9 T1 D9 n: s' N `. H; A/ h0 \0 |: d
这个命令会返回一条oplog记录,其中的ts字段就是最近一次写操作的时间。如果一个节点启动的时候,oplog里没有数据,这个节点会同步其他节点中的所有数据。% N [; {, }" p% Z, d' `' S, N
7 H6 e* ^; J* q o' {
选择同步源节点 1 P1 C& o1 {: Y: F9 \1 p - O8 |4 [4 P, f. U9 d! K1 N Replica Sets中的节点从距离它“最近”的节点同步数据,这个“最近”是通过ping的时间来判断的。在节点之间的心跳检测中,会记录ping某个节点和收到响应的时间,通过这个时间的长短,来确定距离的远近,时间越长视为距离越远。知道了和节点之间的距离,再通过如下的算法,来确定可以同步数据的源节点: } R% S( i2 q# y2 S
% }5 v: {, P! l2 T }" K( `for each member that is healthy: 9 I7 V) ^; \. H f if member[state] == PRIMARY1 i) s" s4 V0 ?8 o+ T( c- U
add to set of possible sync targets4 H+ W) G5 `* `4 U
/ [( ^5 c0 L- q% o+ D
if member[lastOpTimeWritten] > our[lastOpTimeWritten]) v. G6 Q4 V+ H9 k
add to set of possible sync targets% A( S- }: U+ V5 W x4 `
2 E, I+ |! J, s; I- F
sync target = member with the min ping time from the possible sync targets " U9 t+ _4 S9 p) o* }- F- G2 { $ K$ f9 ^1 o) {# [7 W# ` 对于节点是否健康,MongoDB各个版本的判断依据有所不同,但都是为了找到能够正常运行的节点。- z: K" Z. ~ C1 e4 d! B0 n7 b