在 mongodb 3.0 複製中,當輔助節點宕機時如何進行選舉
**情況:**我在兩台電腦上有一個 MongoDB 複製集。
- 一台電腦是擁有主節點和仲裁器的伺服器。此伺服器是實時伺服器,並且始終處於開啟狀態。複製中使用的本地 IP 是
192.168.0.4
.- 第二個是輔助節點所在的 PC,每天執行幾個小時。複製中使用的本地 IP 是
192.168.0.5
.**我的期望:**我希望實時伺服器成為我的應用程序數據互動的主要點,無論 PC 的狀態如何(是否可達,因為 PC 是輔助的),所以我想確保伺服器的節點始終是主要的。
以下是 的結果
rs.config()
:liveSet:PRIMARY> rs.config() { "_id" : "liveSet", "version" : 2, "members" : [ { "_id" : 0, "host" : "192.168.0.4:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 10, "tags" : { }, "slaveDelay" : 0, "votes" : 1 }, { "_id" : 1, "host" : "192.168.0.5:5051", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : 0, "votes" : 1 }, { "_id" : 2, "host" : "192.168.0.4:5052", "arbiterOnly" : true, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : 0, "votes" : 1 } ], "settings" : { "chainingAllowed" : true, "heartbeatTimeoutSecs" : 10, "getLastErrorModes" : { }, "getLastErrorDefaults" : { "w" : 1, "wtimeout" : 0 } } }
如果這很重要,我還將儲存引擎設置為 WiredTiger。
**我實際得到的是什麼,以及問題:**當我關閉 PC 或終止其 mongod 程序時,伺服器上的節點將成為次要節點。
以下是我在連接到主節點的 shell 時殺死 PC 的 mongod 程序時伺服器的輸出:
liveSet:PRIMARY> 2015-11-29T10:46:29.471+0430 I NETWORK Socket recv() errno:10053 An established connection was aborted by the software in your host machine. 127.0.0.1:27017 2015-11-29T10:46:29.473+0430 I NETWORK SocketException: remote: 127.0.0.1:27017 error: 9001 socket exception [RECV_ERROR] server [127.0.0.1:27017] 2015-11-29T10:46:29.475+0430 I NETWORK DBClientCursor::init call() failed 2015-11-29T10:46:29.479+0430 I NETWORK trying reconnect to 127.0.0.1:27017 (127.0.0.1) failed 2015-11-29T10:46:29.481+0430 I NETWORK reconnect 127.0.0.1:27017 (127.0.0.1) ok liveSet:SECONDARY>
我有兩個疑問:
- 考慮到MongoDB 文件的這一部分:
副本集使用選舉來確定哪個集成員將成為主要成員。選舉在啟動副本集之後發生,並且在主節點變得不可用的任何時候發生。
選舉發生在主節點不可用時(或在啟動時,但這部分與我們的情況無關),但主節點始終可用,所以為什麼會發生選舉。
- 考慮到同一文件的這一部分:
如果大部分副本集不可訪問或不可用,則副本集無法接受寫入,並且所有剩餘成員都變為只讀。
考慮到“成員變為只讀”部分,我有兩個節點向上和一個向下,所以這也不應該影響我們的複制。
**現在我的問題是:**當 PC 上的節點無法訪問時,如何將伺服器上的節點保持為主節點?
更新: 這是
rs.status()
.現在這使行為變得明顯,因為無法訪問仲裁器。
liveSet:PRIMARY> rs.status() { "set" : "liveSet", "date" : ISODate("2015-11-30T04:33:03.864Z"), "myState" : 1, "members" : [ { "_id" : 0, "name" : "192.168.0.4:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 1807553, "optime" : Timestamp(1448796026, 1), "optimeDate" : ISODate("2015-11-29T11:20:26Z"), "electionTime" : Timestamp(1448857488, 1), "electionDate" : ISODate("2015-11-30T04:24:48Z"), "configVersion" : 2, "self" : true }, { "_id" : 1, "name" : "192.168.0.5:5051", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 496, "optime" : Timestamp(1448796026, 1), "optimeDate" : ISODate("2015-11-29T11:20:26Z"), "lastHeartbeat" : ISODate("2015-11-30T04:33:03.708Z"), "lastHeartbeatRecv" : ISODate("2015-11-30T04:33:02.451Z"), "pingMs" : 1, "configVersion" : 2 }, { "_id" : 2, "name" : "192.168.0.4:5052", "health" : 0, "state" : 8, "stateStr" : "(not reachable/healthy)", "uptime" : 0, "lastHeartbeat" : ISODate("2015-11-30T04:33:00.008Z"), "lastHeartbeatRecv" : ISODate("1970-01-01T00:00:00Z"), "configVersion" : -1 } ], "ok" : 1 } liveSet:PRIMARY>
發生了什麼
我在評論中詢問了 OP 提供的輸出
rs.status()
這樣做的原因是,一旦單個成員關閉,主節點就會恢復到次要狀態。很明顯,集群失去了選舉新主節點所需的法定人數。只有當副本集的原始成員的另外一個投票成員不可用或變得不可用時,才會出現這種情況。
事實證明,主伺服器無法訪問相關副本集的仲裁器,在 PC 關閉後,主伺服器是副本集唯一剩餘的成員(從它的角度來看)。因此,不可能在配置的副本集成員的法定人數下舉行選舉,因此它恢復到輔助狀態。
如何預防
- 始終
rs.status()
在設置副本集後執行。rs.status()
遇到副本集問題時始終執行- 始終進行失敗測試(直至失去寫入功能)並確保您的應用程序優雅地處理這些情況(就像 OP 所做的那樣)
使用這些規則,您將消除使用副本集時可能面臨的絕大多數問題。
就個人而言,我認為MongoDB Inc. 的 Cloud Manager是生產環境的必備工具,因為它可以立即顯示 OP 遇到的此類問題並且內置警報。
邊注
永遠,永遠(是的,這意味著沒有任何例外,無論原因似乎是合理的)將仲裁器放在同一副本集的數據承載節點上。
想像一下帶有仲裁器的節點和數據傳輸節點發生故障。
如果您有一個 3 成員副本集,您將沒有原始成員的法定人數,您的剩餘成員將自動恢復為輔助,失去故障轉移功能。
在 5 個成員的副本集中,兩個投票成員將被淘汰。只要所有其他人都啟動並執行就可以了,對嗎?除了,這不好。如果另一個節點發生故障,您將再次失去法定人數。因此,只有兩個節點發生故障,其他兩個節點或多或少變得無用。考慮到當今虛擬伺服器的價格(即使是最小的也足以執行仲裁器),這根本沒有意義。無論如何,您將支付 4 個數據承載節點並失去故障轉移功能,因為您試圖節省總成本的一小部分。
使用 7 個成員的副本集,這些成本在總成本中只佔很小的一部分。
結論:讓仲裁器與數據承載節點在同一台機器上執行只是一個糟糕的商業決策,即使拋開技術方面的問題也是如此。