设为首页 收藏本站
查看: 990|回复: 0

[经验分享] Mongodb源码分析--Replication之OpLog

[复制链接]

尚未签到

发表于 2015-7-5 14:27:49 | 显示全部楼层 |阅读模式
在之前的文章中,介绍了关于master-slave模式下的主从端代码的执行流程,因为当时篇幅所限,未对oplog的数据结构以及mongodb的local数据库作过多阐述,而这可能会让不知道其内容的朋友看代码时云里雾里找不到头绪,今天我专门用一篇文章来大致解释一下(这些内容可能会在后面章节中有所涉及)。
      首先了解一个local数据库:
      在mongod中,出于特殊目的(复制机制),保留性使用了local数据库。当使用认证机制时,对local数据库等同于认证admin数据库。
        
      当使用复制集(Replica sets)模式时,其会使用下面的local数据库:
   


local.system.replset 用于复制集配置对象存储 (通过shell下的rs.conf()或直接查询)
local.oplog.rs       一个capped collection集合.可在命令行下使用--oplogSize 选项设置该集合大小尺寸.
local.replset.minvalid  通常在复制集内使用,用于跟踪同步状态(sync status)
      主从复制模式(Master/Slave):


    * Master
          o local.oplog.$main 存储"oplog"信息
          o local.slaves  存储在master结点上相应slave结点的同步情况(比如syncTo时间戳等)
    * Slave
          o local.sources 从结点所要链接的master结点信息(可通过--source配置参数指定)
    * Other
          o local.me 未知待查:)
          o local.pair.* (replica pairs选项,目前已不推荐使用)
   
    除了了解local之外,还有oplog的数据结构(存储在local.oplog.$main)要解释一下:
     { ts : ..., op: ..., ns: ..., o: ... o2: ...  }
    上面就是一条oplog信息,复制机制就是通过这些信息来进行节点间的数据同步并维护数据一致性的,其中:
   


    ts:8字节的时间戳,由4字节unix timestamp + 4字节自增计数表示。
        这个值很重要,在选举(如master宕机时)新primary时,会选择ts最大的那个secondary作为新primary。
    op:1字节的操作类型,例如i表示insert,d表示delete。
    ns:操作所在的namespace。
    o:操作所对应的document,即当前操作的内容(比如更新操作时要更新的的字段和值)
    o2: 在执行更新操作时的where条件,仅限于update时才有该属性  
    其中op,可以是如下几种情形之一:



     "i": insert
     "u": update
     "d": delete
     "c": db cmd
     "db":声明当前数据库 (其中ns 被设置成为=>数据库名称+ '.')
     "n":  no op,即空操作,其会定期执行以确保时效性
  
   了解了这些内容之后,大家通过下面方式来验证一下上面的这些内容:
   1.本地构造复制集



  D:\mongodb\bin>mongod --replSet myoplogs --dbpath d:\mongodb\db  
   2.使用mongo连接到上面结点并初始化



    D:\mongodb\bin>mongo
      MongoDB shell version: ...
      connecting to: test
      > rs.initiate()  
   3.查看oplog复制集信息



> use local
     switched to db local
   > db.oplog.rs.find()  {"ts" : { "t" : 1306207268000, "i" : 1 }, "h" : NumberLong(0), "op" : "n", "ns" : "", "o" : { "msg" : "initiating set" } }

  
  4.开始添加记录



  > use test
     switched to db test
   > db.foo.insert({x:1})
   > db.foo.update({x:1}, {$set : {y:1}})
   > db.foo.update({x:2}, {$set : {y:1}}, true)
   > db.foo.remove({x:1})  
   5.查看上面操作生成的oplog信息:



   > use local
     switched to db local
   > db.oplog.rs.find()  
    { "ts" : { "t" : 1306207268000, "i" : 1 }, "h" : NumberLong(0), "op" : "n", "ns" : "", "o" : { "msg" : "initiating set" } }
    { "ts" : { "t" : 1306207310000, "i" : 1 }, "h" : NumberLong("3138280161636515857"), "op" : "i", "ns" : "test.foo", "o" : { "_id" : ObjectId("4ddb244d2d0d00000000551a"), "x" : 1 } }
    { "ts" : { "t" : 1306207314000, "i" : 1 }, "h" : NumberLong("772196482295043060"), "op" : "u", "ns" : "test.foo", "o2" : { "_id" : ObjectId("4ddb244d2d0d00000000551a") }, "o" : { "$set" : { "y" : 1 } } }
    { "ts" : { "t" : 1306207317000, "i" : 1 }, "h" : NumberLong("7272647888810218413"), "op" : "i", "ns" : "test.foo", "o" : { "_id" : ObjectId("4ddb2455be10000000002f6a"), "x" : 2, "y" : 1 } }
    { "ts" : { "t" : 1306207325000, "i" : 1 }, "h" : NumberLong("3083832920223263240"), "op" : "d", "ns" : "test.foo", "b" : true, "o" : { "_id" : ObjectId("4ddb244d2d0d00000000551a") } }

   注:其它常用命令:db.oplog.$main.help()  db.printReplicationInfo();

   知道上面这些内容之后,我们来大致看一下mongod在源码层面上是如何存储oplog的,先请看下面文件:
   oplog.cpp:顾名思义,它的作用就是存储和读取oplog,其主要包括方法:
      
   createOplog():初始化本地local.oplog.$main 集合信息,包括大小尺寸等
   _logOpOld():   用于在master/slave模式下向local.oplog.$main 添加oplog信息
   _logOpObjRS():    用于在replset模式下向local.oplog.rs模式下添加oplog 信息
   append_O_Obj():向已存在的obj对象后追加新的对象元素信息(主要用于更新类型的oplog)
   pretouchN()及pretouchOperation():某些情况下MongoDB会锁住数据库。如果此时正有数百个请求,则它们会堆积起来,造成许多问题。这里使用下面的优化方式来避免锁定:每次更新前,先查询记录。查询操作会将对象放入内存,于是更新则会尽可能的迅速。在"主/从"部署方案中,从节点可以使用“-pretouch”参数运行,这也可以得到相同的效果。
   

       下面就借助源码来进一步深入了解,首先回顾一下之前这篇文章中的所描述的这段代码:
  


   //repl.cpp
   void startReplication() {
        /* if we are going to be a replica set, we aren't doing other forms of replication. */
        if( !cmdLine._replSet.empty() ) {
            ......
            //绑定函数,oplog.cpp ->_logOpRS(), 用于处理replset类型的oplog操作
            newRepl();
            return;
        }
        //绑定函数,oplog.cpp ->_logOpOld(), 用于处理master-slave类型的oplog操作
        oldRepl();
     上面的newRepl()及oldRepl()主要是实现函数绑定,它会将oplog.cpp文件中的_logOpRS和_logOpOld方法加以绑定(本人猜测可能因为1.6版之后引入了replset,造成方法名称的重新命名和分配,导致这里用这种方式对老的方法进行“过渡”)。
     接着上面方面会调用oplog.cpp文件的createOplog()来构造master的“local.oplog.$main”集合,如下:     
   


    //如果是master,则构造并启动线程方法replMasterThread
     if ( replSettings.master || replPair ) {
            if ( replSettings.master )
                log(1) = 0 );
                if( countdown > 0 ) {
                    countdown--; // was pretouched on a prev pass
                }
                else {
                    const int m = 4;
                    if( tp.get() == 0 ) {
                        int nthr = min(8, cmdLine.pretouch);
                        nthr = max(nthr, 1);
                        tp.reset( new ThreadPool(nthr) );
                    }
                    vector v;
                    oplogReader.peek(v, cmdLine.pretouch);
                    unsigned a = 0;
                    while( 1 ) {
                        if( a >= v.size() ) break;
                        unsigned b = a + m - 1; // v[a..b]
                        if( b >= v.size() ) b = v.size() - 1;
                        tp->schedule(pretouchN, v, a, b);
                        DEV cout

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-83421-1-1.html 上篇帖子: NoSQL之【MongoDB】学习(二):DML和查询操作说明 下篇帖子: 开篇有益:为什么选择MongoDB?
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

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