设为首页收藏本站

爱吱声

 找回密码
 注册
搜索
查看: 3089|回复: 4
打印 上一主题 下一主题

[信息技术] Replica Set的数据同步

[复制链接]

该用户从未签到

跳转到指定楼层
楼主
发表于 2012-9-18 13:20:29 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    上一篇文章,我们了解了replica set是如何选举出primary的。当primary被选举出来,就开始处理系统中的写数据的请求,secondary要及时的同步这些写到primary中的最新的数据,保持MongoDB中数据的一致性,那么secondary是如何进行数据同步的呢?接下来我们详细分析这个问题。! h$ j# `5 H- }1 V/ m- j- \& O2 [: v
/ t) F& U+ Q! R: O
同步" o* J6 R3 e2 y* P9 D
! Z* R# J& T9 p, A3 l8 D
    一个secondary在正常运行时,会选择replica set中的一个节点,从这个节点中叫做local.oplog.rs的collection,拉取oplog同步日志。获得同步日志后,进行下边的三项操作:7 `7 |. w  G' j& ?
    执行op日志
" L' C  ]3 K" N$ p; C    将op日志写入到自己的oplog中(也就是local.oplog.rs)8 z4 i( u9 p5 C- H; B& T
    请求下一个op日志+ o+ u7 O/ e  c

  @3 l, Z5 M3 u8 U% a" p3 t    如果在第一步执行完毕,第二步还没有执行完的时候,secondary宕机了,那么在secondary重新恢复之后,会认为第二步的写操作还没有执行,重新开始执行第二步。在MongoDB的设计中,oplog的操作是具有幂等性的,也就是说将oplog中的某一条操作记录执行多次,不会影响结果的正确性。
/ ?4 ]+ Z8 ^; d+ x& X+ T; D6 S3 I- D/ k4 p
    比如说,有一个数据是{counter:1},我们在primary中,对这个数据执行了操作{$inc:{counter:1}},就是把counter字段的值增加1,结果是{counter:2}。Oplog不会记录inc操作,而是直接记录{$set:{counter:2}}。因此,对于oplog中的操作记录,无论执行多少次,都不会影响结果的正确性。: O' \$ S) `8 N- z

! \5 E3 Y7 P" H' z, |w参数
. o# s& E4 m6 I6 a7 e( y
. b1 `  S7 s* E5 c+ f    当我们在MongoDB中执行一个写操作时,默认情况下,写操作指令发送后,就认为写操作执行成功了。为了保证系统可用性和数据安全性,我们可以更改配置,当写操作在n个节点(n包括primary,如果n=1,那就是在primary执行成功后返回)都执行成功后,才返回成功。这个配置的命令如下:
6 C- \5 t+ t4 D# ]" U! Z/ a8 m) Udb.foo.runCommand({getLastError:1, w:2})
% _$ e  e% @; h; r- M& I' }+ g, m/ n; s. P8 h
    在更改了这个配置之后,执行写操作的流程如下:" k5 Y# E) a! X7 Z5 @0 I! s

/ k% v. q# a- Y: t% B( o    在primary上完成写操作;
" a$ E  e* E! @5 Y    写操作被记录在primary的oplog中,oplog中包含一个ts字段,记录了写操作发生的时间t;& B$ s6 \; C2 p" u  Q' m
    客户端在primary中执行{getLastError:1, w:2}命令,primary完成了写操作,只要再有一个节点完成写操作,就可以满足w:2     了;
8 o6 Y9 @/ I/ C+ `6 b    secondary从primary获得oplog,获得上一次操作的记录;1 w0 s( E& {. i9 R1 w! `+ x: [1 ~
    secondary执行oplog中刚才那一条时间t的操作;
5 d7 b. O; R: d1 G7 }    secondary从primary的oplog中获取时间t之后的log,条件为{ts:{$gt:t}};
/ b; [+ \3 \9 l1 Y# u! E7 E+ b' ]    primary知道了secondary已经成功执行了时间t之前的oplog,因为secondary已经在请求时间t之后的oplog了;. m, X9 S# F8 {
    getLastError知道primary与secondary都完成了这次写操作,于是 w:2 的条件满足了,向客户端返回成功。
4 X8 O! }7 K7 ]. `6 r1 }, e
- G4 X6 G( b/ [9 H0 c0 H" l启动
/ m8 V; F  k! _1 l% [$ Q' s+ Q8 x' o9 o! ]$ ]: `
    当在现有的某个replica set中加入一个新节点并启动时,这个新节点会查看自己的local.oplog.rs collection,执行一个叫 lastOpTimeWritten 的命令,查找到它最近的一条被secondary同步过的写操作。5 b! m2 \# Z, j% P

* j/ F7 w/ j, J3 g$ A    这个命令会返回一条oplog记录,其中的ts字段就是最近一次写操作的时间。如果一个节点启动的时候,oplog里没有数据,这个节点会同步其他节点中的所有数据。
/ K* f; r" }9 h3 l
6 Y) z$ H) \8 l1 t# g选择同步源节点
5 C4 ~$ [1 s; K- k, ^7 u3 N: g3 j) f  F
& k; r8 W2 y/ u' a    Replica Sets中的节点从距离它“最近”的节点同步数据,这个“最近”是通过ping的时间来判断的。在节点之间的心跳检测中,会记录ping某个节点和收到响应的时间,通过这个时间的长短,来确定距离的远近,时间越长视为距离越远。知道了和节点之间的距离,再通过如下的算法,来确定可以同步数据的源节点:. Z5 d* F; P9 ?6 E, Q

8 s# R0 z6 N% f/ I/ @$ K6 d" k9 _for each member that is healthy:2 U0 D1 Z. w) d: s# v& E$ |
    if member[state] == PRIMARY  z/ S# l" a" w6 n
        add to set of possible sync targets
# Q. |- Z0 @( `4 k; |8 x4 k2 c! H. J3 R
    if member[lastOpTimeWritten] > our[lastOpTimeWritten]
; V+ p1 V8 P: y8 v/ m: p) {+ j        add to set of possible sync targets
7 w6 l6 y. X4 J, \. k0 W( X
- i- d! Y  F9 e4 [% q- a0 _# X$ ^sync target = member with the min ping time from the possible sync targets$ m( N' F& k/ U( R5 l

9 |1 r. W) ]* c4 l/ {    对于节点是否健康,MongoDB各个版本的判断依据有所不同,但都是为了找到能够正常运行的节点。
& J- ]& [2 F+ Q6 l- U
7 M' c9 \2 W8 I% m& t5 w  N" Y: K    我们可以通过运行db.adminCommand({replSetGetStatus:1})命令来查看当前的节点状况,在secondary上运行这个命令的时候,能够看到syncingTo这个字段,这个字段的值就表示secondary节点同步数据的源节点。5 A% w, Z; V3 h+ b  V
+ A3 \( s- p7 x
链式同步8 J% r7 P+ [1 ~8 d" r# k/ R! g/ Z

' g) }$ a; k- ?' ]! Y* }3 C    前边所说的内容,都是假设有一个primary和一个secondary,这种情况下的同步过程比较简单,但是如果有2个secondary或者更多,那么这个过程就要复杂的多。$ w4 Y. J4 d& ?, S" R& S& ~$ O

, J2 v( U9 |* u6 R& @; b    我们用w:3来说明这个问题。比如S1和S2节点是secondary节点, P节点是primary节点,S1节点从P节点同步数据,S2节点从S1节点同步数据。这样P -> S1 -> S2 之间就形成了一个链。如果我们设定w为3,那么除了primary写入数据,还需要有两个secondaris完成同步,才可以返回成功。那么P节点如何能知道S2节点已经从S1节点同步成功了呢?# V9 |" w/ {6 f( c) [- k& _8 A3 i# Y
  v$ y, E6 n3 f. n2 O5 Z( H
    MongoDB通过oplog同步协议来解决上述的多个节点同步的问题。5 K/ n  \0 M2 G+ M
3 ?' M% Y3 Q2 X% L6 A$ A! v
    当S2从S1同步数据时,S2会给S1发送一个特殊的握手消息,“Hi,我是S2,我要从你这同步数据了,把我也算到w参数里边吧。”
- q' R) k/ N. \1 L# S/ `& |9 F. T- X
    当S1收到这个消息的时候,会说,“我不是primary节点,我可以把你这个计数转到我的同步源中去。”然后S1打开一个到P的新的连接,然后对P说,“这个连接你就当是S2的吧,把S2也算到w的计数中。”这个时候,S1和P之间有两个连接,一个是S1自己的,一个是为S2建立的。! Y) C6 ^/ [  q

- e. h& v/ E" M    当P执行完写操作之后,S1首先会获取到P的oplog,执行完这个写操作之后,会告诉P,我已经执行完了。然后S2从S1获取到最新的oplog,同样执行这个写操作,执行完之后,告诉S1,我已经执行完了。S1在收到S2执行完毕的消息后,就通过S1代替S2建立的和P的连接,告诉P,我是代替S2建立的连接,现在S2也执行完这个写操作了。这个时候,P就知道已经有P、S1和S2都完成了这个写操作,w:3已经满足了,然后返回成功,完成这次操作。/ ^. y8 @$ G0 H; M

) x9 ]$ A3 z9 `# J    具体三个节点间的连接如下图:+ w2 c- N* X4 E4 M3 R5 `- h: g
    S2                  S1               P
' F% v3 q% R- _; X5 J/ P& e
                             <====>
( i+ u7 e7 Y+ h# m0 w
         <====>       <---->
9 K; D5 K9 B0 T' T/ Y

1 d3 g  x6 w0 L    S1和P之间有两条通道,双线那条是真正的同步连接,单线那条是一个虚拟连接。7 y9 z+ R& n: s

( }# l; J) B2 q8 H7 r( s, f" f+ H) Y+ Q/ x" ?& s) h3 L! Q1 j* H
Reference,
: p* C, ~3 P6 _7 Y. W
$ W2 `: X4 G2 R( Z9 p% s[0] Replica Set Internals Bootcamp Part IV: Syncing
/ r( T2 }" H/ l  c! V2 whttp://www.kchodorow.com/blog/2012/05/07/replica-set-internals-bootcamp-part-iv-syncing/: c2 }% R2 |7 ]& R3 M

评分

参与人数 1爱元 +10 学识 +5 收起 理由
不爱吱声 + 10 + 5 谢谢!有你,爱坛更精彩

查看全部评分

  • TA的每日心情
    擦汗
    前天 12:36
  • 签到天数: 1577 天

    [LV.Master]无

    沙发
    发表于 2012-9-18 13:33:28 | 只看该作者
    哇...,没想到这里都能看到这类高水准文章。4 `( g, {1 S) O& G, T
    可以偷懒不去搜索了。

    该用户从未签到

    板凳
     楼主| 发表于 2012-9-18 13:34:48 | 只看该作者
    四处张望 发表于 2012-9-18 13:33 / ]: |9 u' T5 B. |+ ~
    哇...,没想到这里都能看到这类高水准文章。2 g' F3 V. e2 z6 B
    可以偷懒不去搜索了。

      A7 H/ u' E+ Z7 X' R* z前期写的一次性发出来了,后续的还要等一等。写的慢啊,哈哈
  • TA的每日心情
    擦汗
    前天 12:36
  • 签到天数: 1577 天

    [LV.Master]无

    地板
    发表于 2012-9-18 13:46:57 | 只看该作者
    shengnan007 发表于 2012-9-18 13:34 ) Q5 U0 S5 `9 e% o
    前期写的一次性发出来了,后续的还要等一等。写的慢啊,哈哈
    + u+ n0 K3 ], A' [
    邓侃在西河的文章,对我启发很大。可惜最近两年没有实践的机会,mongo db也就是浅尝辄止。现在有这般好帖,正好

    该用户从未签到

    5#
    发表于 2012-9-18 14:44:18 | 只看该作者
    电脑小白路过学习。。。

    手机版|小黑屋|Archiver|网站错误报告|爱吱声   

    GMT+8, 2025-1-23 00:48 , Processed in 0.033486 second(s), 18 queries , Gzip On.

    Powered by Discuz! X3.2

    © 2001-2013 Comsenz Inc.

    快速回复 返回顶部 返回列表