MongoDb部署機器修改網域名稱可能導致move chunk失敗

來源:互聯網
上載者:User

版本:  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為改網域名稱前的_id, id2為改網域名稱後的_id. 那麼前後兩次做過move chunk的時候分別有一個optime1和optime2. 這樣 (id1,optime1) 和 (id2,optime2)都在_slaves裡面, 但是表示的是同一個mongod, 而且optime1是早就過時的了. 這樣就導致了_slaves裡面的元素個數比實際secondary多.(ps: _slaves的元素不止這兩個值, 只是這兩個值是最重要的, 確定唯一的)

5) 經過確認, 我們的叢集確實經曆過搬機房, 修改網域名稱, 重啟過這些部署了secondary的機器. 加上我自己在另外搭建的叢集上面實驗, 2次修改網域名稱之後就發生了move chunk失敗的現象, 加上日誌的輸出, 和第4)點的結論一致. 


到此為止, 總結一下
聯絡另外一篇博文<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同步完成發生了錯誤.

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.