一 数据同步 一个健康的secondary在运行时,会选择一个离自己最近的,数据比自己新的节点进行数据同步。选定节点后,它会从这个节点拉取oplog同步日志,具体流程是这样的: a.执行这个op日志 b.将这个op日志写入到自己的oplog中(local.oplog.rs) c.再请求下
当我们在MongoDB时执行一个写操作时,默认会直接返回成功,同时也可以通过设置w参数,指定这个写操作同步到几个节点后才返回成功。如下:
db.foo.runCommand({getLastError:1, w:2})
上面例子就是执行getLastError命令,使其在上一个写操作同步到两个节点上后再返回。不同的客户端可能在写法上不太一样,不过这个功能应该都是有的。对于重要数据,可以考虑采用这样的方式,通过牺牲一部分写性能来提升数据的安全性。
这个功能是如何实现的呢,primary节点是如何知道数据同步了几份呢?在调用上面命令时,实际上MongoDB内部执行了如下的一些流程:当一个新节点启动并加入到现在的Replica Sets中时,这时候新启动的节点会查看自己的oplog,通过一个叫 lastOpTimeWritten 的命令查找到它最近的一条写操作。这个命令你也可以随便在命令行执行:
> rs.debug.getLastOpWritten()
这个命令会返回一条oplog记录,其中的ts字段就是最近一次写操作的时间了。
如果你这个节点是全新的,没有数据,那么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,但是由于版本兼容的原因,沿用了这个错误的名字)
vcq9vfjQ0M2ssr2ho8v50tTH6b/2u+G4tNTT0rvQqaGjPC9wPgo8cD4KICAgICAgICAgscjI587Sw8fT0L3ateNBo6zOqnByaW1hcnm92rXjo6zIu7rzQr3atePOqnNlY29uZGFyeb3ateOjrMv8tNNBvdq1482ssr3K/b7do6zNrMqx09bT0HNlY29uZGFyeb3ateNDo6zL/LTTzazKx3NlY29uZGFyebXEQr3atePNrLK9yv2+3aGj1eLR+UEtJmd0O0ItJmd0O0PWrrzkvs3QzrPJwcvSu7j2wbTKvbXEzayyvb3hubmho8jnufvO0sPHyei2qHfOqjOjrMTHw7RBvdq148jnus7E3NaqtcBDvdq149LRvq2000K92rXjzayyvbPJuabBy8TYo788L3A+CgogICAgICAgINXiysfNqLn9b3Bsb2fNrLK90K3S6cC0yrXP1rXEo6zO0sPH08PNqMvXtcTT79HUwLS94srN0rvPwm9wbG9ntcTNrLK90K3S6aGjCgo8YnI+CgoKICAgYS61sUO000LNrLK9yv2+3cqxo6xDu+HU2tCt0unW0LbUQsu1o7rO0tKqtNPE49Xizayyvcr9vt3By6OsyOe5+9C0stnX99PQd7LOyv21xLuwo6zO0rXEzayyvdKyy+PJz7DJoaMKICAgYi7Iu7rzQrvhu9i08Mu1o7rO0rK7ysfSu7j2cHJpbWFyeb3ateOjrM7Su+Gw0cTjtcTV4rj2vMbK/deqtb3O0rXEzayyvdS0yc/IpTxicj4KICAgYy7Iu7rzQtTZttRBtPK/qtK7uPbQwrXEway906OssqLH0rbUQcu1o7rV4rj2way908Tjvs21sbPJysdDtcSwyaOs0rLL49K7uPa8xsr91Np3wO+hozxicj4KICAgZC7V4sqxuvLU2kG/tMC0o6y+zdPQwb249sGsvdPBrLW9y/vJz8Pmo6zSu7j2ysdCo6zSu7j2ysfQ6cTitcRDo6zV4sG9uPbBrL3TtrzE3LGouObL+8u1zeqzycHLzayyvbLZ1/ehozxicj4KPHA+CiAgICAgICAgtbHSu7j20LSy2df31NpByc/WtNDQuvOjrELK18/IzayyvbW91eK49rLZ1/e1xG9wbG9no6zWtNDQzeq687vhuObL30GjrM7S1rTQ0M3qwcuho8i7uvNDzazR+bTTQsnPu/HIobW9QrXEb3Bsb2ejrNKy1rTQ0MHL1eLSu8z10LSy2df3o6zIu7rzy/u45svfQqOsztLWtNDQzerBy6OsQtTaytW1vdXiuPbP7NOmuvOjrLvhzai5/bjVssW/qs2otcTQ6cTizai1wLj6Qcu1o6zO0srH0OnE4rXEQ73ateOjrM7S0rLN6rPJ0LSy2df3wcuho9XiyrG68kG+zdaqtcCjrEGhokKhokPI/bj2vdq147a8zeqzydC0stnX98HLoaN3o7oztcTM9bz+wvrX46OsyLu687e1u9i4+LX308NnZXRMYXN0RXJyb3K1xL/Nu6e2y6OszeqzydXitM6y2df3oaM8L3A+CjxwPgq+38zlyP249r3ateO85LXEway908jnz8LNvKO6PC9wPgo8cHJlIGNsYXNzPQ=="brush:java;">C B A
<====>
<====> <---->
B和A之间有两条通道,双线那条是真正的同步连接,单线那条是一个虚拟连接。
注意:MongoDB这种链式同步结构类&#20284;于Hadoop中HDFS中数据块的流式复制,这样的好处是可以大大减轻主节点的压力,提高数据同步的速度。
上面就是当前的Replica Sets同步的内部实现,在后续这一块MongoDB还会进行一些新特性的开发。在2.2版本中,会提供replSetSyncFrom命令,让用户可以手动设置一个secondary的同步源。使用方法大概是这样:
> db.adminCommand({replSetSyncFrom:"otherHost:27017"})