關於MongoDb Replica Set的容錯移轉叢集——實戰篇

來源:互聯網
上載者:User

標籤:過程   tin   cas   mes   進階   oda   提示符   stat   mongo   

如果你還不瞭解Replica Set的相關理論,請猛戳傳送門閱讀筆者的上一篇博文。

因為Replica Set已經屬於MongoDb的進階應用,下文中關於MongoDb的基礎知識筆者就不再贅述了,請參考MongoDb Manual。

下面分各種情境講述如何建立一個Replica Set。

Standalone到Replica Set

這是相對簡單的一種情況。如果你剛剛在生產環境應用MongoDb,很有可能適用於這種情境。

一台獨立的MongoDb執行個體變為Replica Set的首位成員很容易,需要兩個步驟:

1. 運行參數中加入--replicaSet <set name>。如:

mongod --dbpath /var/lib/mongo/ --replicaSet rs0 --fork

如果開啟了認證模式則需要額外的一步:為執行個體配置一個Key,作為今後不同執行個體間認證的根據。

Key的原理很簡單,只要不同執行個體擁有相同的Key,則認證成功。沒有公私密金鑰交換等等麻煩的過程。

產生Key也很簡單,任何一個檔案存了字串都可以成為Key。只要保證不同執行個體使用相同的Key檔案即可。

添加Key請使用參數 --keyFile=<file path>。產生Key請參考官方文檔。

2. 進入MongoDb命令列進行初始化,大致情況如下:

$ mongo localhost:27011MongoDB shell version: 2.4.9connecting to: localhost:27011/test> rs.initiate(){    "info2" : "no configuration explicitly specified -- making one",    "me" : "YX-ARCH:27011",    "info" : "Config now saved locally.  Should come online in about a minute.",    "ok" : 1}

稍等片刻,叢集初始化完成,斷行符號後提示符從">"變為

rs0:PRIMARY> 

表示該執行個體已經成為一個Primary結點。此時叢集中只有一個結點,不存在投票問題,所以無論怎麼折騰這個結點都會保持Primary狀態。但到下一步就要小心了。

rs0:PRIMARY> rs.add("YX-ARCH:27012"){ "ok" : 1 }

此時第二個結點YX-ARCH:27012已加入叢集,可以使用以下命令查看叢集狀態:

rs0:PRIMARY> rs.status(){    "set" : "rs0",    "date" : ISODate("2014-01-24T07:21:01Z"),    "myState" : 1,    "members" : [        {            "_id" : 0,            "name" : "YX-ARCH:27011",            "health" : 1,            "state" : 1,            "stateStr" : "PRIMARY",            "uptime" : 353,            "optime" : Timestamp(1390548012, 1),            "optimeDate" : ISODate("2014-01-24T07:20:12Z"),            "self" : true        },        {            "_id" : 1,            "name" : "YX-ARCH:27012",            "health" : 1,            "state" : 2,            "stateStr" : "SECONDARY",            "uptime" : 49,            "optime" : Timestamp(1390548012, 1),            "optimeDate" : ISODate("2014-01-24T07:20:12Z"),            "lastHeartbeat" : ISODate("2014-01-24T07:21:00Z"),            "lastHeartbeatRecv" : ISODate("2014-01-24T07:21:00Z"),            "pingMs" : 0,            "syncingTo" : "YX-ARCH:27011"        }    ],    "ok" : 1}

可見27011此時是Primary身份,而27012則是Secondary身份。如果想改變結點的角色,則需要修改叢集的配置。首先將叢集配置調出:

rs0:PRIMARY> conf = rs.conf(){    "_id" : "rs0",    "version" : 2,    "members" : [        {            "_id" : 0,            "host" : "YX-ARCH:27011"        },        {            "_id" : 1,            "host" : "YX-ARCH:27012"        }    ]}

上篇講過每個叢集結點都有一個priority屬性,預設為1。要改變一個執行個體的角色,我們只需要給新的執行個體設定一個高於當前Primary的priority就可以實現了。

注意因為只有Primary結點是可寫的,所以重新設定群集的操作只能在Primary結點上進行。

rs0:PRIMARY> conf.members[1].priority = 22rs0:PRIMARY> rs.reconfig(conf)Fri Jan 24 15:25:36.069 DBClientCursor::init call() failedFri Jan 24 15:25:36.070 trying reconnect to localhost:27011Fri Jan 24 15:25:36.070 reconnect localhost:27011 okreconnected to server after rs command (which is normal)rs0:SECONDARY>

可見短暫的罷工後命令列自動重新串連到當前執行個體,但從提示符可以看出,當前執行個體已經變為Secondary角色。我們來隨便逛逛Secondary裡面的內容(Test是筆者自己建立的庫)

rs0:SECONDARY> show dbsTest    0.203125GBlocal    2.0771484375GBrs0:SECONDARY> use Testswitched to db Testrs0:SECONDARY> show collectionsFri Jan 24 15:28:32.697 error: { "$err" : "not master and slaveOk=false", "code" : 13435 } at src/mongo/shell/query.js:128

如果用過Master Slave叢集的讀者應該發現了,Slave結點是可讀的,而Secondary結點預設卻不可讀。要解決這個問題,只需要簡單的一步:

rs0:SECONDARY> rs.slaveOk()rs0:SECONDARY> show collectionssystem.indexestest

至此一個擁有2個執行個體的MongoDb Replica Set就建立完成了。那麼我們來試試傳說中的故障恢複特性。

假設Primary結點因故停止工作了(我們手動幹掉它)

$ ps awx | grep mongo12546 ?        Sl     0:08 mongod --config mongodb1.conf14201 ?        Sl     0:07 mongod --config mongodb2.conf  <--Primary在這20258 pts/2    Sl+    0:00 mongo localhost:2701224186 pts/0    S+     0:00 grep --color=auto mongo$ kill 14201

在美好的幻想中,Secondary應該馬上即位成為新的Primary吧?

$ mongo localhost:27011MongoDB shell version: 2.4.9connecting to: localhost:27011/testrs0:SECONDARY> 

神馬?還是Secondary?說好的故障恢複呢?

或許是姿勢不對?那我們重新啟動兩個執行個體,這回先當掉Secondary試試

$ ps awx | grep mongo12546 ?        Sl     0:10 mongod --config mongodb1.conf  <-- Secondary在這20258 pts/2    Sl+    0:00 mongo localhost:2701227024 ?        Sl     0:00 mongod --config mongodb2.conf27413 pts/0    S+     0:00 grep --color=auto mongo$ kill 12546
$ mongo localhost:27012MongoDB shell version: 2.4.9connecting to: YX-ARCH:27012/testrs0:SECONDARY> 

神馬?Primary降級為Secondary了?如果沒看上篇的讀者心裡應該暗暗罵娘了吧?”禽獸,這算什麼錯誤恢複叢集?明明就是兩個都死了!“

那麼我們再把死掉那個Secondary複活試試?你會發現Primary又回來了。上篇說過,提升到Primary是一個很嚴格的過程,一旦出現2個Primary的情況,叢集就廢了。所以寧可錯殺1000也不可放過一個。

所以實際發生的事情是這樣的:當叢集中僅有的2個執行個體掛掉一個,剩下的一個並不能判斷自己的存活狀況,因為也可能是自己跟網路斷開了串連造成的。另外一個執行個體說不定還活得好好的呢。因此沒有辦法,暫時讓自己成為Secondary活下去吧。

一旦另外一個執行個體恢複生存,兩個執行個體就可以互相證明對方存活,因此還是priority較高的那個繼續扮演Primary。

為了避免這種悲劇的發生,Arbiter的必要性就體現出來了。

自然你可以在群集中再加一個Secondary,讓三個執行個體互相證明存活性,這樣不僅剛才的問題可以解決 ,自動切換也是可以達成的。但是要成為Secondary的伺服器可是對效能有一定要求的,現實情況下這樣做可能性價比並不高。那麼還是添加一個Arbiter吧,它的存在只是為了投票選舉,不佔用額外資源,放在資源有限的虛擬機器中就足夠了。如果有多個Arbiter為不同的叢集服務,也可以放進同一台虛擬機器中以節省資源。

rs0:PRIMARY> rs.addArb("YX-ARCH:27013"){ "ok" : 1 }

順帶一提,剛才我們的操作中一直使用的是我的機器名"YX-ARCH",而不是localhost。這裡localhost是被禁止使用的,原因是這樣:

如果在命令列查看叢集資訊

rs0:PRIMARY> rs.status(){    "set" : "rs0",    "date" : ISODate("2014-01-24T08:08:32Z"),    "myState" : 1,    "members" : [        {            "_id" : 0,            "name" : "YX-ARCH:27011",            "health" : 1,            "state" : 2,            "stateStr" : "SECONDARY",            "uptime" : 1010,            "optime" : Timestamp(1390550842, 1),            "optimeDate" : ISODate("2014-01-24T08:07:22Z"),            "lastHeartbeat" : ISODate("2014-01-24T08:08:30Z"),            "lastHeartbeatRecv" : ISODate("2014-01-24T08:08:32Z"),            "pingMs" : 0,            "syncingTo" : "YX-ARCH:27012"        },        {            "_id" : 1,            "name" : "YX-ARCH:27012",            "health" : 1,            "state" : 1,            "stateStr" : "PRIMARY",            "uptime" : 1758,            "optime" : Timestamp(1390550842, 1),            "optimeDate" : ISODate("2014-01-24T08:07:22Z"),            "self" : true        },        {            "_id" : 2,            "name" : "YX-ARCH:27013",            "health" : 1,            "state" : 7,            "stateStr" : "ARBITER",            "uptime" : 70,            "lastHeartbeat" : ISODate("2014-01-24T08:08:31Z"),            "lastHeartbeatRecv" : ISODate("2014-01-24T08:08:32Z"),            "pingMs" : 0        }    ],    "ok" : 1}

我們可以看到叢集中有三個執行個體,分別是

YX-ARCH:27011YX-ARCH:27012YX-ARCH:27013

當使用某種語言,比如C#來串連MongoDb的時候,哪些伺服器可用的資訊並不是從連接字串中來的,而是Driver會得到一份類似以上的資訊來判斷哪些伺服器可用。可見,如果添加到叢集中的地址是localhost:27011,那麼Driver也會認為localhost:27011是一台可用的執行個體。但多數情況下應用和資料庫都是分開的,這會造成應用徒勞地從自己機器上的27011連接埠去找MongoDb服務,顯然是不可能成功的。關於擷取可用執行個體的問題,筆者之前寫過一篇日誌,請戳傳送門。

以上已經建立了一個完整的叢集,望大家用得開心。但作為摸爬滾打了多年的IT人,我是萬萬不敢在沒有考慮後路的情況下就往生產環境上使用一個自己不知根知底的系統的。進兵前先考慮退路是常識,所以還是要考慮一下萬一的情況下,如何從Replica Set退回Standalone的情境。

Replica Set 到 Standalone

考慮上述3個執行個體的Replica Set,它雖然可以在任何一個執行個體當掉的情況下保持正常工作,那如果2個執行個體同時當掉呢?什麼,不可能有這麼衰?前人的經驗告訴我們:在IT的世界裡,只要有可能發生的事情就一定會發生。

所以當災難來臨的時候如果你兩手空空,怎麼能不被打個鼻青臉腫?那麼,操傢伙開幹。

無論Primary還是Secondary,只要去掉--replicaSet即可馬上變回Standalone的狀態。如果要徹底回到Standalone狀態,還應該刪除資料庫目錄下的local.*檔案(注意刪除操作必須在MongoDb停止服務的情況下進行)。

如果不刪這些檔案,MongoDb也會正常工作,但TTL Collection會工作不正常。此外筆者還沒發現有什麼問題。

現實是,在多數情況下我們是不會希望從Replica Set變回Standalone的。如果你真的這麼做了,那大概只有一個原因:踩了上文中的某個雷,導致叢集變成完全唯讀了。你期望暫時變回Standalone狀態,以便不影響生產環境的日常操作,等其他的執行個體恢複之後再加上--replicaSet參數變回Replica Set狀態。很不幸,如果你是這個目的,那麼你馬上會踩第二個雷。

我們知道Replica Set的原理是傳送oplog並重做,而oplog只有在master/slave或replica set模式下才會產生。所以當你回到standalone模式下時所做的任何事情都是沒有oplog的,這意味著當你再次回到Replica Set模式中時會丟失部分資料。

更氣人的是:MongoDb不會阻止你做這樣的事情,它會讓你成功回到Replica Set狀態。無論是Primary還是Secondary都可以。你甚至可以在Secondary變成Standalone期間修改它的內容後再讓它回到ReplicaSet,這樣它的內容就跟其他執行個體不一致!

知道這個坑之後來看看簡單粗暴的解決辦法:

1. 停止MongoDb執行個體

$ sudo systemctl stop mongodb

2. 進入資料庫檔案夾刪除local.*

$ cd /var/lib/mongodb/$ rm local.* -rf

3. 把--replicaSet參數重新添加回設定檔

4. 重啟啟動MongoDb

sudo systemctl start mongodb

此時MongoDb變回普通的非Replica Set執行個體

5. 按上文的方法重新初始化Replica Set並建立叢集。

雖然複雜了點,但這也是沒有辦法的事情,千萬不可走捷徑。

Master/Slave到Replica Set

Master/Slave到Replica Set其實和Standalone到Replica Set並無兩樣,所有步驟完全相同。要注意的只有一點,一旦Master變成Primary,Slave將不再從它同步資料。也就是說從執行

> rs.initiate()

的一刻開始,Slave就成為了當時資料庫的快照,不再更新。所以在生產環境中應用這個操作時,要注意資料不同步可能帶來的危害,必要時必須停機維護。遇到這類問題時:

方案一:可以把原來使用Slave的應用程式切換為使用Master,優點是可以全程不間斷服務。缺點是要求你的Master效能足夠好。

方案二:如果想使用方式情節一但Master的效能又不夠好,可以使用一台足夠強大的新機器先做為Slave,然後所有系統停機維護,這時把Slave變為Master,所有服務使用新的Master繼續工作。之後再從這台Master開始向Replica Set的轉換工作。優點是停機時間可以比較短。缺點嘛……服務中斷了……另外所有系統指向新的Master時視系統規模可能修改比較多,容易有疏漏。

方案三:完全停機進行master/slave到replica set的轉換。所有Secondary完成複製後再開始重新服務。這當然是最輕鬆的方案了,自然停機時間也是最長的。

後記

MongoDb作為新興的非關係型資料庫近年來發展迅速。新特性層出不窮。想瞭解它的方方面面,最簡單的方法是讀它的User manual。雖然是件費力的事情,但也是沒有辦法的事情,就像上面提到的一樣,想走捷徑的結果往往是掉進坑裡。

希望筆者的血淚史為小夥伴們提供前車之鑒,本文如有不正確之處歡迎指正。

 

  

關於MongoDb Replica Set的容錯移轉叢集——實戰篇

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

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.