上面的介紹的資料同步(http://www.cnblogs.com/guoyuanwei/p/3293668.html)相當於傳統資料庫中的備份策略,mongoDB在此基礎還有自動故障轉移的功能。在複製集概述那一節提到過心跳"lastHeartbeat"欄位,mongoDB就是靠它來實現自動故障轉移的。 mongod例項每隔2秒就向其它成員傳送一個心跳包以及通過rs.staus()中返回的成員的”health”值來判斷成員的狀態。如果出現複製集中primary節點不可用了,那麼複製集中所有secondary的節點就會觸發一次選舉操作,選出一個新的primary節點。如上所配置的複製集中如果primary節點當機了,那麼就會選舉secondary節點成為primary節點,arbiter節點只是參與選舉其它成員成為primary節點,自己永遠不會成為primary節點。如果secondary節點有多個則會選擇擁有最新時間截的oplog記錄或較高許可權的節點成為primary節點。oplog記錄在前面複製集概述中已經描述過,關於複製集中節點許可權配置的問題可在複製集啟動的時候進行設定,也可以在啟動後重新配置,這裡先略過這一點,集中精力討論故障轉移。
如果是某個secondary節點失敗了,只要複製集中還有其它secondary節點或arbiter節點存在,就不會發生重新選舉primary節點的過程。
下面模擬兩種失敗場景:一是secondary節點的失敗,然後過一段時間後重啟(時間不能無限期,否則會導致oplog.rs集合嚴重滯後的問題,需要手動才能同步);二是primary節點失敗,故障轉移發生。
先分析第一種情況的測試,當前複製集的配置情況如下:
(1)rs0:PRIMARY> rs.conf()
{
"_id" : "rs0",
"version" : 3,
"members" : [
{
"_id" : 0,
"host" : "Guo:40000" //primary節點
},
{
"_id" : 1,
"host" : "Guo:40001" //secondary節點
},
{
"_id" : 2,
"host" : "Guo:40002", //arbiter節點
"arbiterOnly" : true
}
]
}
(2)通過Kill掉secondary節點所在的mongod例項,通過rs.status()命令檢視複製集狀態,secondary節點狀態資訊如下:
"_id" : 1,
"name" : "Guo:40001",
"health" : 0,
"state" : 8, //表示成員已經down機
"stateStr" : "(not reachable/healthy)",
"uptime" : 0,
"optime" : {
"t" : 1376838296,
"i" : 1
},
"optimeDate" : ISODate("2013-08-18T15:04:56Z")
(3)接著通過primary節點插入一條記錄:
rs0:PRIMARY> db.scores.insert({stuid:2,subject:"english",score:100})
(4)再次檢視複製集狀態資訊rs.status(),可以看到primary成員節點上oplpog資訊如下:
"optime" : {
"t" : 1376922730,
"i" : 1
},
"optimeDate" : ISODate("2013-08-19T14:32:10Z"),
與上面down機的成員節點比較,optime已經不一樣,primary節點上要新於down機的節點。
(5)重新啟動Kill掉的節點
>mongod --config E:\mongodb-win32-i386-2.4.3\configs_rs0\rs0_1.conf
查詢複製集狀態資訊rs.status(),觀看節點"Guo:40001"的狀態資訊如下:
"_id" : 1,
"name" : "GUO:40001",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 136,
"optime" : {
"t" : 1376922730, //與上面primary節點一致了
"i" : 1
},
"optimeDate" : ISODate("2013-08-19T14:32:10Z"),
說明secondary節點已經恢復,並且從primary節點同步到了最新的運算元據。進一步通過查詢secondary節點上local資料庫上的oplog.rs集合來進行驗證,發現多了一條下面這樣的記錄:
{ "ts" : { "t" : 1376922730, "i" : 1 }, "h" : NumberLong("-451684574732211704"),
"v" : 2, "op" : "i", "ns" : "students.scores", "o" : { "_id" : ObjectId("52122c
6a99c5a3ae472a6900"), "stuid" : 2, "subject" : "english", "score" : 100 } }
這正是在primary節點上插入的記錄,再次證明資料確實同步過來了。
接下來測試第二種情況:
(1)將primary節點Kill掉。
查詢複製集的狀態資訊rs.status()
"name" : "Guo:40000",
"health" : 0,
"state" : 8,
"stateStr" : "(not reachable/healthy)"
欄位"health"的值為0,說明原來的primary節點已經down機了。
"name" : "Guo:40001",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY"
欄位"stateStr"值為"PRIMARY",說明原來secondary節點變成了primary節點。
(2)在新的primary節點上插入一條記錄
rs0:PRIMARY> db.scores.insert({stuid:3,subject:"computer",score:99})
(3)重新恢復"Guo:40000"節點(原來的primary節點)
>mongod --config E:\mongodb-win32-i386-2.4.3\configs_rs0\rs0_0.conf
再次檢視複製集狀態rs.status()
"name" : "Guo:40000",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 33,
"optime" : {
"t" : 1376924110,
"i" : 1
},
當"Guo:40000"例項被重新啟用後,變成了secondary節點,oplog也被同步成最新的了。說明當primary節點故障時,複製集能自動轉移故障,將其中一個secondary節點變為primary節點,讀寫操作繼續在新的primary節點上進行。原來primary節點恢復後,在複製集中變成了secondary節點。
上面兩中情況都得到了驗證,但是有一點要注意,mongDB預設情況下只能在primary節點上進行讀寫操作。對於客戶端應用程式來說,對複製集的讀寫操作是透明的,預設情況它總是在primary節點上進行。 mongoDB提供了很多種常見程式語言的驅動程式,驅動程式位於應用程式與mongod例項之間,應用程發起與複製集的連線,驅動程式自動選擇primary節點。當primary節點失效,複製集發生故障轉移時,複製集將先關閉與所有客戶端的socket連線,驅動程式將返回一個異常,應用程式收到這個異常,這個時候需要應用程式開發人員去處理這些異常,同時驅動程式會嘗試重新與primary節點建立連線(這個動作對應用程式來說是透明的)。假如這個時候正在發生一個讀操作,在異常處理中你可以重新發起讀資料命令,因為讀操作不會改變資料庫的資料;假如這個時候發生的是寫操作,情況就變得微妙起來,如果是非安全模式下的寫,就會產生不確定因素,寫是否成功不確定,如果是安全模式,驅動程式會通過getlasterror命令知道哪些寫操作成功了,哪些失敗,驅動程式會返回失敗的資訊給應用程式,針對這個異常資訊,應用程式可以決定怎樣處置這個寫操作,可以重新執行寫操作,也可以直接給使用者暴出這個錯誤。