日期:2014-05-16  浏览次数:20490 次

MongoDb部署机器修改域名可能导致move chunk失败

版本:  2.2.0


以下内容涉及之前的文章<MongoDb move chunk 故障分析和处理 (SERVER-5351)> .


在前面的文章<MongoDb move chunk 故障分析和处理 (SERVER-5351)> 提到的问题, 我们move chunk失败了. 问题的调查直指变量 vector _slaves, 该变量只会在关闭mongod和启动mongod的时候做clear操作, 而其他时候只会增或者改.从我们move chunk日志里面看出来, 我们的mongod的_slaves变量里面应该有 5/2+1 或者 4/2+1 个元素, 那么怎么来的呢? 我们明明只有3个secondary.


经过代码的追查, 发现了几个点

1) 在secondary mongod的rs.me里有唯一一条记录, 我们暂时叫它作 M.  M有两个关键的元素 _id 和 host, _id是这个mongod的一个唯一id, 由ObjectId生成, host是这个mongod部署的机器的域名.

2) 在move chunk的过程中primary会向secondary获取一个remoteid, 也就是上面提到的_id

3) secondary在启动后, 一旦需要获取记录M, 则会去rs.me里面查, 查询代码是这样的:

bool replHandshake(DBClientConnection *conn) {
    string myname = getHostName();
    BSONObj me;
    {
        Lock::DBWrite l("local");
        if ( ! Helpers::getSingleton( "local.me" , me ) || //从数据库local.me里面取出来机器的id, 这个就是所谓的_remoteId
            ! me.hasField("host") ||
            me["host"].String() != myname ) {//这条记录里面的host和机器当前的host不同, 则需要清空重新设置, 此时就会重置_id
            Helpers::emptyCollection("local.me"); 

            BSONObjBuilder b;
            b.appendOID( "_id" , 0 , true );//如果从local.me里面取不到id, 则创建一个id, 作为所谓的_remoteId
            b.append( "host", myname );
            me = b.obj();
            Helpers::putSingleton( "local.me" , me );
        }
    }
    BSONObjBuilder cmd;
    //把id封装为 {"handshake": _remoteId}, 这个和我们在日志里面看到的一致, _remoteId在bsonobj里面的key是 "handshake"
    cmd.appendAs( me["_id"] , "handshake" );
    if (theReplSet) {
        cmd.append("member", theReplSet->selfId());
    }

    BSONObj res;
    bool ok = conn->runCommand( "admin" , cmd.obj() , res );//执行handshake命令
    return true;
}

4) 所以, 如果某一个作为secondary的mongod的机器改过域名, 那么这个mongod就前后两次的rs.me里文档的_id就不一样, 我们成为id1为改域名前的和id2为改域名后的.那么前后两次做过move chunk的时候分别有一个optime1和optime2. 这样 (id1,optime1) 和 (id2,optime2)都在_slaves里面, 但是表示的是同一个mongod, 而且optime1是早就过时的了. 这样就导致了_slaves里面的元素个数比实际secondary多.(ps: _slaves的元素不止这两个值, 只是这两个值是最重要的, 确定唯一的)

5) 经过确认, 我们的集群确实经历过搬机房, 修改域名, 重启过这些部署了secondary的机器.


到此为止, 总结一下
联系另外一篇博文<MongoDb move chunk 故障分析和处理>, _slaves这个变量是primary用来确定自己需要去跟踪多少个secondary的同步状态, map<Ident,OpTime> _slaves 的key值是secondary机器的唯一标识, value是Oplog同步到哪一条状态. 只要是曾经有一次作为secondary去跟primary同步数据, 那么primary的_slaves里面就会记录这个机器的Ident(是_remoteId和host(在我们的集群里面用ip, 我们ip没有修改)和ns(日志显示是local.oplog.rs)的组合), Ident确定了唯一的一条_slaves记录.

如果修改过域名, 那么就会导致新的Ident被插入到_slaves里面, 其对应的opTime会非常过时, 导致movechunk的时候, 要的等带多数secondary同步完成发生了错误.