下面介绍分别介绍一下这两种复制的配置方式: Master-Slave(主从)模式: 一个server可以同时为master和slave。一个slave可以有多个master(不推荐,可能会产生不可预期的结果)。 配置选项: --master 以主服务器方式启动 --slave 以从服务器方式启动 --autoresync:自动重新sync,因为该操作会copy 主服务器上的所有document,比较耗时,在10分钟内最多只会进行一次。 --oplogSize:指定master上用于存放更改的数据量,如果不指定,在32位机上最少为50M,在64位机上最少为 1G,最大为磁盘空间的5%。 --source 主服务器地址(与--slave组合使用) --only 仅限于同步指定数据库(下面示例为test库) --slavedelay 同步延时 下面是本人在本地为了测试方便所使用的配置参数 Master: IP->10.0.1.103 mongod --dbpath=d:\mongodb\db --master --oplogSize 64
Slave: IP->10.0.4.210 mongod --dbpath=d:\mongodb\db --slave --source 10.0.1.103:27017 --only test --slavedelay 100
补充:受限的master-master复制,这种模式对插入、查询及根据_id进行的删除操作都是安全的。但对同一对象的并发更新无法进行。Mongo 不支持完全的master-master复制,通常情况下不推荐使用master-master模式,但在一些特定的情况下master-master也可用。master-master也只支持最终一致性。配置master-master只需运行mongod时同时加上--master选项和 --slave选项。如下: mongod --dbpath=d:\mongodb\db --port 27017 --master --slave --source localhost:27018 mongod --dbpath=d:\mongodb\db --port 27018 --master --slave --source localhost:27017
Replica pairs模式 以这种方式启动后,数据库会自动协商谁是master谁是slave。一旦一个数据库服务器断电,另一个会自动接管,并从那一刻起起为master。万一另一个将来也出错了,那么master状态将会转回给第一个服务器。以这种复制方式启动mongod的命令如下: 配置选项: mongod --pairwith
另外这种日志只保存会“改变数据库状态”的操作。查询操作不会记录在oplog中。 好了,了解这些知识之后,我们就来开始看一下如何调试master-slave模式的源码,首先要在vs2010中打开mongod项目,并将启动参数中设置如下: --master --oplogSize 64 (master IP为10.0.1.103) 如下图: 之后编译该项目,启动该主服务结点,如下: 接着我们可以在本地或另外一台机器上启动一个slave结点: mongod --dbpath=d:\mongodb\db --slave --source 10.0.1.103:27017 --only test --slavedelay 100
下面介绍一下master(主服务端)的代码执行流程。首先我们打开instance.cpp文件,找到下面方法: //instance.cpp // Returns false when request includes 'end' void assembleResponse( Message &m, DbResponse &dbresponse, const SockAddr &client ) { ...... if ( op == dbQuery ) { if ( handlePossibleShardedMessage( m , &dbresponse ) ) return; receivedQuery(c , dbresponse, m ); } //服务端(master) 收到message执行相关查询操作 else if ( op == dbGetMore ) { if ( ! receivedGetMore(dbresponse, m, currentOp) ) log = true; } ..... }
看过本系列开头那几篇BLOG的朋友,会看出上面方法其实在mongodb的crud操作中都会执行到,更多内容可以参见这篇BLOG,这里不再赘述。 当slave 从结点发送同步复制请求时,master会执行上面的dbGetMore操作,从主库中的oplog中获取相应日志并返回给slave结点,下面是receivedGetMore()方法的具体实现: //instance.cpp bool receivedGetMore(DbResponse& dbresponse, Message& m, CurOp& curop ) { StringBuilder& ss = curop.debug().str; bool ok = true; //参见:Mongodb源码分析--消息(message)中的 查询更多(document)消息结构相关内容 //http://www.cnblogs.com/daizhj/archive/2011/04/02/2003335.html DbMessage d(m); //完整的集合名称,形如:"dbname.collectionname" const char *ns = d.getns(); //返回的document数 int ntoreturn = d.pullInt(); //在REPLY消息中的Cursor标识符,其必须来自于数据库 long long cursorid = d.pullInt64(); ss << ns << " cid:" << cursorid; if( ntoreturn ) ss << " ntoreturn:" << ntoreturn; time_t start = 0; int pass = 0; bool exhaust = false; QueryResult* msgdata;//查询结果 while( 1 ) { try { readlock lk; Client::Context ctx(ns); //执行GetMore查询 msgdata = processGetMore(ns, ntoreturn, cursorid, curop, pass, exhaust); } catch ( GetMoreWaitException& ) { exhaust = false; massert(13073, "shutting down", !inShutdown() ); if( pass == 0 ) { start = time(0); } else { if( time(0) - start >= 4 ) { // after about 4 seconds, return. this is a sanity check. pass stops at 1000 normally // for DEV this helps and also if sleep is highly inaccurate _disibledevent=>// return occasionally so slave can checkpoint. pass = 10000; } } pass++; DEV sleepmillis(20); else sleepmillis(2); continue; } catch ( AssertionException& e ) { exhaust = false; ss << " exception " << e.toString(); msgdata = emptyMoreResult(cursorid); ok = false; } break; }; //将查询结果集绑定到message对象 Message *resp = new Message(); resp->setData(msgdata, true); ss << " bytes:" << resp->header()->dataLen(); ss << " nreturned:" << msgdata->nReturned; //将上面的消息对象指针绑定到dbresponse dbresponse.response = resp; dbresponse.responseTo = m.header()->id; if( exhaust ) { ss << " exhaust "; dbresponse.exhaust = ns; } return ok; }
可以看出,通过对message的解析找出相应的cursorid,因为mongodb如果发现游标为tailable(类型)时,会cache该cursor而不是关闭它,这主要是考虑到当下次slave请求来时,直接从cache中获取该cursor以提升效率并用它来作为继续获取后续oplog操作信息。上面方法在执行结束处会将获取到的oplog结果封装到message中并返回。但其如何获取,就要分析下面方法了: //query.cpp QueryResult* processGetMore(const char *ns, int ntoreturn, long long cursorid , CurOp& curop, int pass, bool& exhaust ) { exhaust = false; //在map
上面代码有些长,但其目的很明确,就是针对指定的cursor进行遍历。这里mongodb会为每个slave保存一个cursor,并且其在遍历完成后将最后一条oplog的时间戳作为当前slave在local.slaves中的更新标识信息(syncedTo),来标识当前slave的更新情况。(注:首次同步时全部复制会执行copyDatabase,复制master db上的所有document)。该方法运行截图如下:
另外需要解释的是,master结点貌似并不会使用slave发来的syncedTo来过滤capped collection中的旧oplog(指小于syncedTo时间戳)的数据,而是使用tailable类型的cursor来解决如果持续获取后续新增oplog操作信息。前者的主观臆测让我在源码中兜了一个圈子,因为我一直主观认为mongod会执行类似查询操作来过滤相应旧oplog的时间戳信息,并将结果集返回给slave端。现在看来master只是不断返回后续添加到cap collection中oplog(有可能是out of sync的情况而引发slave地点执行resync操作),而最终的过滤判断操作完全交给了slave端。这一点会在我下一篇文章中有所介绍。
好了,今天的内容到这里就告一段落了。在接下来的文章中,将会介绍slave端是如何发起同步操作,以及最终如何使用获取到的oplog来构造本机数据的。 参考链接:
http://www.mongodb.org/display/DOCS/Replication
http://www.mongodb.org/display/DOCS/Master+Slave
http://www.snailinaturtleneck.com/blog/2010/10/12/replication-internals/ http://www.snailinaturtleneck.com/blog/2010/10/14/getting-to-know-your-oplog/ http://www.snailinaturtleneck.com/blog/2010/08/02/replica-sets-part-2-what-are-replica-sets/ 原文链接:http://www.cnblogs.com/daizhj/archive/2011/06/13/mongodb_sourcecode_repl_master_run.html 作者: daizhj, 代震军 微博: http://t.sina.com.cn/daizhj Tags: mongodb,c++,Replica,master-slave
最新评论