解決方案| MongoDB PSA 架構痛點以及如何應對?

一. 背景
最近 MongoDB 羣裏面有羣友遇到2次重啓 MongoDB 後一直處於實例恢復狀態(應用 OPLOG ),多達幾天甚至更長才完成重啓,通常 MongoDB 副本集三個實例作爲標準,重啓主庫會發生重新選出新主節點(通常在12s內完成)重新對外服務,通常不符合官方標準化或者內部發生異常導致的。經過了解副本集採用 PSA 架構且存在一個數據從節點不可達的情況(甚至有的從節點宕機幾個月沒有發現),來分析這些情況以及如何應對。

主要包括以下內容:( WT 存儲引擎下版本是3.2、3.4、3.6、4.0、4.2爲主,4.4、5.0也存在)。

PSA 架構下從節點宕機後,重啓主庫爲什麼會這麼久
PSA 架構還有哪些問題
PSA 架構下如何緩解內存壓力以及推薦 PSS 方案
模擬 PSA 架構下重啓主庫實例後長時間等待的情況並通過不同方案來解決

二. PSA 架構重啓主庫爲什麼會這麼久
備註:有個提前是從庫宕機存在一定週期且在此期間產生大量的髒數據。

2.1 官方 PSA 架構介紹

當數據節點宕機且 enableMajorityReadConcern ( WT 3.2版本開始默認開啓),內存壓力增大,這裏只是說內存壓力增大,沒有進一步說明影響,比如說壓力超過內存限制後如何處理、對性能的影響以及超長時間等待重啓之類。(由於沒有源碼能力,相關東西只能通過 jira 、專家交流以及自己實驗來驗證)。
圖片

2.2 爲什麼會造成主庫內存壓力?

用於從副本集或者集羣中讀取數據時,能夠允許讀取到被大多數節點收到並被確認的數據,對應關係型數據庫裏面提交讀。

注意:這裏只是允許而已(數據庫提供的能力,3.2版本纔開始支持)。

客戶端是否需要根據實際 readConcern 級別,不管我們是否需要這種 majorityReadConcern 級別,數據庫 WT 引擎已經維護這些信息在內存中(默認是開啓)。

當 PSA 副本集中存在一個數據節點宕機時,主庫內存中數據的 Majority commit point 是無法推進的,此時 checkpoint 是不能將這些數據持久化(內存中髒數據無法更新到數據文件中),同時 OPLOG 會保留所有變更操作,如果從庫宕機時間長且主庫很忙,OPLOG 會增長很大(4.0版本開始會超過配置大小)。

PSA 此時 checkpoint 不能對 Majority commit point 後的數據進行持久化,主庫必須維護最近 Majority commit point 的快照提供給讀,所以內存壓力會增大和內存使用,最終這些內存數據溢出,此時 MongoDB 利用 SWAP 技術將內存中置換到磁盤上(將內存數據置換到磁盤上 WiredTigerLAS.wt ),所以性能會下降,如從庫宕機時間長,此時主庫性能也慢,同時磁盤空間也會暴漲。可能會考慮重啓實例(通常情況下重啓能解決大部分問題),那麼實例可能重啓需要等特幾天甚至更長時間才能完成。因爲數據沒有持久化,重啓的話就需要進行實例恢復,那麼就會出現開頭說重啓好多天都沒有完成的悲劇。重啓過程這個問題會被無限循環。(4.4開始重構來緩解這個問題,使用 WiredTigerHS.wt 來替代)。最壞的情況可能會導致實例 OOM 。

三. PSA 架構還存在哪些問題
PSA 相比 PSS 少一份副本數據,相對應就 cost down. 這個是最直接好處。例如三機房部署 PSA 架構的副本集或者分片,對應 A 的機器最低配即可,不需要消耗什麼資源。通常三機房採用 PSSSA 或者 PSSSS ,發生故障時優先切換本地機房。

正常情況下 PSA 正常下運行與 PSS 架構下無差別。主要出現 S 節點不可達以及長延遲情況會存在異常,除了內存壓力增大造成性能的影響以及跟超長等待時間重啓外,還如下常見場景:

writeConcern 或者 readConcern 爲 majority ,讀寫會異常。majority 表示數據被副本集成員中大多數節點收到並被主確認。5.0之前版本默認 w:1,表示被主節點確認後表示操作成功,此時此羣出現故障可能會導致寫入主節點被回滾,從而造成數據丟失。所以 w:majority 是保證集羣數據故障時不丟失的必需配置。

那麼 majority 到底是多少個節點?對於 majority 是怎麼計算?爲什麼 PSA 架構下宕機一個數據節點就不滿足 majority ?

majority 節點數=最小值(副本集中所有數據節點具有的投票能力總數與副本集中1加上取整(1/2的具備投票節點總數包括仲裁節點))。默認情況下 PSA 中所有節點都具備投票能力,那麼此時 majority 節點數= min (2,3(1+取整(0.5*3))) =2。從4.2.1版本開始,可以通過 rs.status() 中 writeMajorityCount 、 majorityVoteCount 來看。如果此時宕機一個數據節點或者不可達時,此時 majority 還是2,不會因爲狀態的改變而減少 majority 個數。此時需要滿足 w:majority 的操作要不超時要不永不返回的狀態。

注意:此時數據已經寫入主節點,不管是超時還是永不返回,數據不會被回滾(不考慮事務的場景以及 failover 情況)。

同理3.2版本開始默認開啓 enableMajorityReadConcern ,此時 majority commit point 也不會被推進。所以說 PSA 在一定程度上通過節約成本來降低系統高可用性。當然,說沒有顯式開啓 majority ,是不是就沒有問題?當存在一個數據節點不可達時,有些潛在場景默認是 majortiy 配置且不能修改,例如5.0開始 enableMajorityReadConcern 這個不能被禁用。例如 changestream 要求數據被大多數節點應用,同時也影響分片集羣部分功能。

集羣部分功能異常
分片集羣數據平衡,源或者目標分片中不能滿足大多數成員時,數據平衡或者擴縮分片都會失敗。分片集羣管理,例如 shardCollection 、 dropIndex 等要求 majoriy 都會失敗。分片集羣下 changeStream 同樣會無法捕獲最新數據造成同步延遲。

隱藏丟失數據操作
如果從庫已經宕機 N 時間,此時主庫也宕機了,如果運維人員先啓動老的從庫,那麼會"丟 N 時間"數據,這個數據存在在原主庫,此時原主庫啓動後需要先回滾 N 時間數據才能重新加入到副本集中,通常回滾有限制,大概率會回滾失敗。

四. 如何緩解內存壓力
4.1 緩解內存壓力

避免一個數據節點實例宕機情況下對系統的影響,通過完善的監控及時發現節點異常(宕機、延遲),及時處理故障,否則無能爲力。

禁用 MajorityReadConcern ( PSA 架構來避免內存壓力,同時注意 changeStream ,4.2版本不管這個參數,對於出現問題的集羣或者副本集。

臨時將異常從庫的優先級別與投票都設置爲0(5.0版本由於不能禁用 MajorityReadConcern ,注意這個只能修改下應對從庫宕機或延遲時,來緩解主庫內存壓力以及解決一些配置 majority 場景,但失去高可用,因爲從庫不能被選爲主。適用場景是數據庫需要重啓時存在大量髒數據刷盤或者應用配置 w:majority 時,修改宕機實例優先級別與投票爲0後進行重啓纔可以,如果已經重啓的實例,此時只能等待)。

總結: PSA 解決從庫宕機後如何緩解主庫內存壓力,通過有效監控及時消除故障點,如果沒有及時發現,在重啓前通過方案3來避免長時間重啓問題,針對方案2需要提前規劃好,但對於 majority 場景以及分片模式下操作還是無能爲力,如果從庫宕機很久,此時已強制重啓主庫,此時只能進入躺平狀態去等。其他解決方案需要具體問題具體分析,例如只要系統能寫入數據即可,可以把從實例恢復起來或者搭建空實例。

4.2 推薦方案

儘管通過禁用參數或者修改配置來緩解問題,但存在潛在的問題或者不熟悉的人還是會遇到同樣問題,條件允許情況下,應使用 PSS 取代 PSA 架構能夠解決單一數據節點宕機帶來的影響。如果正在使用 PSA 架構也沒有關係,知道存在問題即可,出現問題能夠知道帶來的影響是什麼即可。

五. 場景模擬解決方案
備註:搭建4.2 PSA 副本集,手動 S 實例關閉並通過 POC 壓測數據,構造20個字段1.1億表。

5.1 查看數據情況

億 show dbs 顯示 POCDB 爲0,這個顯示不合理。datasize:62G ,磁盤上大小爲12K (這個說明數據並沒有寫入到磁盤)。
PRIMARY:[db]POCDB> show dbs
POCDB
POCDB 0.000GB
admin 0.000GB
config 0.001GB
local 36.804GB
[db]POCDB> db.POCCOLL.count();
115155968
[db]POCDB>db.POCCOLL.stats();
{
"ns" : "POCDB.POCCOLL",
"size" : 67368296749,
"count" : 115155968,
"avgObjSize" : 585,
"storageSize" : 12288
}

5.2 查看磁盤上文件大小

備註:LAS 文件有28G,但 POCCOLL 磁盤上12K。
[mongo@xiaoxu123 data]$ ls -lh WiredTigerLAS.wt
mongo mongo 28G 5月 12 16:35 WiredTigerLAS.wt
[mongo@vmt30129 data]$ ls -lh 0--6719021210235564209.wt
mongo mongo 12K 5月 12 17:44 0--6719021210235564209.wt

5.3 驗證 MajorityReadConcern 能否讀取數據

備註:選取最近的數據,使用 readConcern:majority 沒有讀取到數據。

查看 MajorityReadConcern 是否開啓。
db.serverStatus().storageEngine.supportsCommittedReads;
true--表示支持.
PRIMARY:[db]POCDB> db.POCCOLL.find({"_id" : { "w" : 3, "i" :
34238463 }}).
readConcern("majority").count();
0
PRIMARY:[db]POCDB>db.POCCOLL.find({"_id" : { "w" : 3, "i" :
34238463 }}).
count();
1

5.4 查看內存壓力情況

備註:此時沒有讀寫,但通過 mongostat 監控發現 dirty 很高且 used 基本上在95%。
圖片

5.5 查看rs.config裏面信息

主要關注lastStableRecoveryTimestamp:1652326222(ISODate("2022-05-12T03:30:22Z")),當前時間5月13號9點49分,而 recover 時間點是12號11點30+0800

根據 rs.config 裏面相關時間來判斷:重啓實例需要12號11點30分來恢復數據。根據 checkpoint 時間點來看:數據並沒有持久化.但oplog持久化了。

如果此時重啓,需要追1天 oplog ,需要從庫停了一週,那麼需要更久實例恢復,通常來說,重啓是一件很常見事。

rs.config 信息
[db]POCDB> rs.status();
{
"set" : "shard22",
"date" : ISODate("2022-05-13T01:49:15.297Z"),
"heartbeatIntervalMillis" : NumberLong(2000),
"majorityVoteCount" : 2,
"writeMajorityCount" : 2,
"lastStableRecoveryTimestamp" : Timestamp(1652326222, 1),
"lastStableCheckpointTimestamp" : Timestamp(1652326222, 1)
}

5.6 在不知道的情況下重啓服務器

5.6.1 重啓實例進入等待模式

備註:啓動實例後進入無限等待時間窗口,在默認情況下 PSA 中 S 宕機多久,重啓主庫就需要不一定等比例時間,取決於多少髒數據以及服務器性能,因爲進行 OPLOG 回放是並行,也許就是一個簡單維護重啓操作造成業務可能停機以天爲級別的等待窗口。之前羣裏面遇到重啓需要2天+等待窗口,正常業務肯定無法接受。登陸到數據庫直接關閉主實例會報錯(因爲此時數據只有1個主+1仲裁),此時就應該注意到異常,也可能忽略這個錯誤,只重啓即可。如果知道重啓會遇到問題,就不會冒然重啓。
[db]admin> db.shutdownServer()
2022-05-13T09:56:21.930+0800 E QUERY [js] Error: shutdownServer failed: {
"operationTime" : Timestamp(1652406965, 1),
"ok" : 0,
"errmsg" : "No electable secondaries caught up as of 2022-05-13T09:56:21.907+0800. Please use the replSetStepDown command with the argument {force: true} to force node to step down."

如果使用這種方式進行 shutdown 後啓動或者使用 systemctl xx restart 底層實際是 kill 進程。
圖片

5.6.2 完成重啓實例

備註:重啓花30分鐘才完成重啓。注意如果反覆重啓,每次都會從相同 recoveryTimestamp 時間點開始,因爲重啓並不能推進 recoveryTimestamp 時間點,因爲開啓 enableMajorityReadConcern 。

幾個指標:
啓動總共34分:09:59:59到10:33:48。
恢復時間點:WiredTiger recoveryTimestamp. Ts: Timestamp(1652326222, 1)
ISODate ("2022-05-12T03:30:22Z"),當時時間爲13號10點。
壓測程序:5個小時構造115155968插入記錄。
After 18001 seconds, 115155968 new documents inserted - collection has 115155968
6397 inserts per second on average
0 keyqueries per second on average
0 updates per second on average
0 rangequeries per second on average
恢復時間:並沒有需要5小時,因爲純插入,恢復日誌每一個 batch 接近5000。我們正常相對複雜點。所以真實業務恢復起來會慢的。Applied 115162250 operations in 23035 batches 。

5.7 此時該如何做?

備註:此時系統重要指標:RPO、RTO 以及最終 SLA 指標。

5.7.1 對應情況如下

需要多久能恢復到最新數據時間點來提供服務(不允許丟失數據)。需要天級別的恢復,這個對應級別就很低了。這個還不是容災災難恢復,此時只能等待,運維壓力山大。

需要最快恢復系統來提供服務(系統可用性優先) 。重新搞一個新實例就可以或者重啓從庫。對應丟失數據來換取系統可用性,例如日誌類。

5.7.2 後續如何解決這個問題

備註:如無法使用 PSS 代替 PSA 架構,參考前面講過2點。

disable majorityReadConcern 需要重啓實例才生效(5.0之前 PSA 採用此方案,也是官方推薦的方案,另外注意 changestream 以及 readConcern 採用 majority )。

修改從節點的 vote 和 priority 爲0,這個只適合臨時針對實例宕機或者存在大延遲情況下降低主庫的內存壓力。否則此時PSA下主庫宕機無法選出新主,失去天生高可用特性。

結論:經過前面講解,我們知道 PSA 在宕機一個數據節點下才存在相關問題,如果 PSA 實例都是正常,我們無需擔心這些問題。但實例宕機或者服務器故障不是我們能控制,需要考慮這種情況下,我們需要通過何種方式來解決這些問題。當系統出現問題時,第一種方案不太合適,只能適合下一次。相對第二種可以臨時解決問題。

方案1:禁用 majorityReadConcern 參數,來緩解內存壓力但無法解決其他問題,需要重啓實例下一次才生效。
圖片

結論:通過禁用 majorityReadConcern 後,第一次重啓還是花費30分鐘,第二次重啓就瞬間完成啓動。但無法解決第一次重啓長時間等待問題,需要預先規劃並修改參數,當遇到問題時直接重啓即可。在 PSA 架構出現數據節點宕機避免對主節點內存壓力。但存在 majority 的場景還是無法解決,甚至 changestream 、分片集羣下部分功能失效,重要系統還建議 PSS 架構。

方案2:臨時將異常從庫的優先級別與投票都設置爲0來恢復。

5.7.3 查看 config 裏面信息

幾個重要時間:
當時北京時間:9號21點(最新時間)
OPLOG 最後提交時間:7號15點32分
readConcernMajorityOP 時間:7號15點32分
OPLOG應用時間以及持久化時間:9號21點(最新時間)
上一個穩定恢復點時間:7號15點32分
上一個穩定checkpoint時間:7號15點32分

根據 rs.config 裏面相關時間來判斷:重啓實例需要7號15點32分來恢復數據。同時根據 checkpoint 時間點來看:數據並沒有持久化。但 oplog 持久化了。此時重啓需要從7號開始。

rs.config 信息
PRIMARY:[db]admin> new Date();
ISODate("2022-05-09T13:00:31.827Z")
[db]admin> new Date(1651908725*1000);
ISODate("2022-05-07T07:32:05Z")

:PRIMARY:[db]admin> rs.status();
{
"set" : "shard22",
"majorityVoteCount" : 2,
"writeMajorityCount" : 2,
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1651908725, 3642),
"t" : NumberLong(16)
},
"lastCommittedWallTime" : ISODate("2022-05-07T07:32:05.621Z"),
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1651908725, 3642),
"t" : NumberLong(16)
},
"readConcernMajorityWallTime" : ISODate("2022-05-07T07:32:05.621Z"),
"appliedOpTime" : {
"ts" : Timestamp(1652101157, 1),
"t" : NumberLong(16)
},
"durableOpTime" : {
"ts" : Timestamp(1652101157, 1),
"t" : NumberLong(16)
},
"lastAppliedWallTime" : ISODate("2022-05-09T12:59:17.625Z"),
"lastDurableWallTime" : ISODate("2022-05-09T12:59:17.625Z")
},
"lastStableRecoveryTimestamp" : Timestamp(1651908725, 3642),
"lastStableCheckpointTimestamp" : Timestamp(1651908725, 3642),

修改宕機的節點優先級別與投票都位0方案1。

修改從庫的優先級與投票。
PRIMARY:[db]POCDB> cfg=rs.config()
{
"_id" : "shard22",
"protocolVersion" : NumberLong(1),
"writeConcernMajorityJournalDefault" : true,
"members" : [
{
"_id" : 0,
"host" : "10.160.100.149:21002",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 2,
"tags" : { },
"slaveDelay" : NumberLong(0),
"votes" : 1
},
{
"_id" : 1,
"host" : "10.160.100.150:21002",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" :{ },
"slaveDelay" : NumberLong(0),
"votes" : 1
},
{
"_id" : 2,
"host" : "10.160.100.151:21002",
"arbiterOnly" : true,
"buildIndexes" : true,
"hidden" : false,
"priority" : 0,
"tags" : {},
"slaveDelay" : NumberLong(0),
"votes" : 1
}
],
}
PRIMARY:[db]POCDB> cfg.members[1].priority=0
0
PRIMARY:[db]POCDB> cfg.members[1].votes=0
0
PRIMARY:[db]POCDB> rs.reconfig(cfg)

查看 MajorityReadConcern 、Recovery 等信息。
PRIMARY:[db]POCDB> rs.status();
{
"set" : "shard22",
"date" : ISODate("2022-05-09T13:32:08.214Z"),

" majorityVoteCount " :2, 投票節點還是2;" writeMajorityCount " :1,writeMajority 從2變1相應的 MajorityReadConcern 也是變1。
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1652103116, 1),
"t" : NumberLong(16)
},

" lastCommittedWallTime " : ISODate("2022-05-09T13:31:56.255Z"),這個時間也變;
" readConcernMajorityWallTime " : ISODate("2022-05-09T13:31:56.255Z"),這個時間也變了" lastStableRecoveryTimestamp " : Timestamp(1651908725, 3642),恢復點還沒有變;" lastStableCheckpointTimestamp " : Timestamp(1651908725, 3642),checkpoint 點還沒有變。

經過24分鐘刷盤後,checkpoint 終於成功推進。
PRIMARY:[db]admin> rs.status();
{
"set" : "shard22",
"date" : ISODate("2022-05-09T13:56:56.426Z"),
"majorityVoteCount" : 2,
"writeMajorityCount" : 1,
"optimes" : {
"lastCommittedWallTime" : ISODate("2022-05-09T13:56:49.036Z"),
"readConcernMajorityWallTime" : ISODate("2022-05-09T13:56:49.036Z")
"lastAppliedWallTime" : ISODate("2022-05-09T13:56:49.036Z"),
"lastDurableWallTime" : ISODate("2022-05-09T13:56:49.036Z")
},

" lastStableRecoveryTimestamp " : Timestamp(1652104559, 1),已經改變;
" lastStableCheckpointTimestamp " : Timestamp(1652104559, 1),已經改變。

查看 checkpoint 信息與集合信息

備註:共20分鐘刷97G。包括索引與集合2部分,其實刷時間也挺久的,比重啓快10分鐘,相對來說比重啓影響小很多。
圖片

重啓實例

結論:重啓很快並沒有把從從實例宕機後所有 OPLOG 都應用一遍,提示 No oplog entries to apply 。
圖片

總結:至此完成分析 PSA 架構存在問題以及對應方案,不管怎麼應對,當單個數據節點宕機或者長延遲時,在一定程度上犧牲高可用性。在知道 PSA 架構優缺點後,需要在數據一致性與可用性做折中考慮,從5.0開始默認writeConcern從w:1變成 majority 。說明 MongoDB 在設計上更加關注數據一致性,這個改變實際從4.4版本就埋下種子,4.4版本開始 oplog 從默認拉取變成推送模式,在一定程度改善延遲問題。所以說在條件允許下,儘量採用 PSS 架構。但 5.0 PSA 默認 WriteConcern 變成 w:1。有個計算公式:
if [ (#arbiters > 0) AND (#non-arbiters <= majority(#voting-nodes)) ]
defaultWriteConcern = { w: 1 }
else
defaultWriteConcern = { w: "majority" }

5.0分片集羣採用 PSA 出現S宕機時,客戶端寫入 hang ,並沒有按官方文檔描述那樣 PSA 默認寫是 w:1。當初只是驗證 PSA 副本集發現與官方描述一致,但並沒有驗證分片架構,導致存在偏差。
圖片

六. 分析與驗證過程
6.1 驗證 PSA 副本集模式

6.1.1 PSA副本集默認寫關注
shard2:PRIMARY> db.adminCommand({getDefaultRWConcern:1})
{
"defaultReadConcern" : {
"level" : "local"
},
"defaultWriteConcernSource" : "implicit",
"defaultReadConcernSource" : "implicit",
"localUpdateWallClockTime" : ISODate("2022-05-17T06:03:42.239Z"),
},

6.1.2 驗證50 PSA 版本默認 writeConcern

備註:關閉一個數據節點後寫入是否正常。
shard2:PRIMARY> cfg.members[0].stateStr
PRIMARY
shard2:PRIMARY> cfg.members[1].stateStr
(not reachable/healthy)
shard2:PRIMARY> cfg.members[2].stateStr
ARBITER
shard2:PRIMARY> use test
switched to db test

沒有指定 writeConcern 成功寫入。
shard2:PRIMARY> db.testDefaultWriteConcern.insert({w:1})
WriteResult({ "nInserted" : 1 })

指定寫入失敗,說明默認 majority 而是官方說 w:1。
shard2:PRIMARY>db.testDefaultWriteConcern.insert({w:1},{writeConcern:{w:"majority",wtimeout:1000}})
WriteResult({
"nInserted" : 1,
"writeConcernError" : {
"code" : 64,
"codeName" : "WriteConcernFailed",
"errmsg" : "waiting for replication timed out",
"errInfo" : {
"wtimeout" : true,
"writeConcern" : {
"w" : "majority",
"wtimeout" : 1000,
"provenance" : "clientSupplied"
}
}
}
})

6.1.3分片使用單個PSA shard來驗證來驗證默認寫關注級別

查看集羣信息。
mongos> sh.status()
--- Sharding Status ---
shards:

{ "_id" : "shard2",
"host" : "shard2/10.230.10.150:21017,10.230.9.150:21017", "state" : 1,
"topologyTime" : Timestamp(1652772600, 4) }
active mongoses:
"5.0.2" : 1
autosplit:
Currently enabled: yes
databases:
{ "_id" : "config", "primary" : "config", "partitioned" : true }
2、創建分片集合
mongos> sh.enableSharding("xiaoxu")
{
"ok" : 1,
}

mongos> sh.shardCollection("xiaoxu.testDefaultWriteConcern",{_id:"hashed"})
{
"collectionsharded" : "xiaoxu.testDefaultWriteConcern",
"ok" : 1,
}

6.1.4 正常情況插入測試數據。

備註:不管採用默認 writeConcern 還是採用 w:1 或者 w:"majority" 模式都沒有問題。
mongos> use xiaoxu
switched to db xiaoxu
mongos> db.testDefaultWriteConcern.insert({_id:1,name:"xiaoxu"})
WriteResult({ "nInserted" : 1 })
mongos>db.testDefaultWriteConcern.insert({_id:2,name:"xiaoxu"},{writeConcern:{w:"majority",wtimeout:1000}})
WriteResult({ "nInserted" : 1 })
mongos>db.testDefaultWriteConcern.insert({_id:3,name:"xiaoxu"},{writeConcern:{w:1}})
WriteResult({ "nInserted" : 1 })

6.1.5 模擬PSA副本中S宕機的場景來插入數據

備註:手動關閉從實例。
shard2:PRIMARY> cfg.members[0].stateStr
PRIMARY
shard2:PRIMARY> cfg.members[1].stateStr
(not reachable/healthy)
shard2:PRIMARY> cfg.members[2].stateStr
ARBITER

mongos> db.testDefaultWriteConcern.insert({_id:6,name:"xiaoxu"},{w:1})
WriteResult({ "nInserted" : 1 })
mongos>
mongos>
mongos>db.testDefaultWriteConcern.insert({_id:7,name:"xiaoxu"},{w:"majority",wtimeout:10000})
WriteResult({
"nInserted" : 1,
"writeConcernError" : {
"code" : 64,
"codeName" : "WriteConcernFailed",
"errmsg" : "waiting for replication timed out; Error details: { wtimeout: true, writeConcern: { w: "majority", wtimeout: 10000, provenance: "clientSupplied" } } at shard2",
"errInfo" : {
}
}
})

異常:此時沒有指定 writeConcern ,採用默認的行爲.此時寫入 hang 住。
mongos> db.testDefaultWriteConcern.insert({_id:8,name:"xiaoxu"})

圖片

6.1.6 分片下寫入數據默認的 writeConcern 來源哪裏?

備註:查詢發現 defaultWriteConcern 是 w:majority 。這個信息是來自於 config 。而不是 shard 層面,目前 config 是單節點的副本集。嘗試改成 PSA 架構試試?
mongos> db.adminCommand({getDefaultRWConcern:1})
{
"defaultReadConcern" : {
"level" : "local"
},
"defaultWriteConcern" : {
"w" : "majority",
"wtimeout" : 0
},
"defaultWriteConcernSource" : "implicit",
"defaultReadConcernSource" : "implicit",
}

6.1.7 嘗試把 config 副本集改成 PSA 架構

備註:config 副本集中禁止加入仲裁節點,那麼 config 默認是 writeConcern 就是 {w:majority} 。
config:PRIMARY> rs.reconfigForPSASet(2, cfg);
Running first reconfig to give member at index 2 { votes: 1, priority: 0 }
{
"ok" : 0,
"errmsg" : "Arbiters are not allowed in replica set configurations being used for config servers",
"code" : 103,
"codeName" : "NewReplicaSetConfigurationIncompatible",
"$gleStats" : {
"lastOpTime" : {
"ts" : Timestamp(1652693376, 3),
"t" : NumberLong(1)
}

6.1.8 針對分片集羣下 DefaultRWConcern 相關說明

解釋:
連接到 mongos 時沒有顯式指定 writeConcern 時,mongos 使用全局默認設置,這個全局設置來自 config 副本集,而不是底層分片,所有底層分片 PSA 下架構默認 writeConcern:{w:1} 直接被 config 副本集全局設置覆蓋。因爲 config 不支持仲裁,所以默認是 writeConcern:{w:"majority"} 。
圖片

6.1.9 5.0 PSA 出現 S 宕機時,爲了避免上一篇文章提到問題外。還包括如下:

如果客戶端沒有指定 writeConcern 採用默認行爲會導致寫入 hang 的情況應對措施:

採用方案2。注意是臨時的,等從庫恢復及時重置回去,否則當主庫宕機,沒有辦法選出新主,從而影響系統可用性,此時從宕機或者機器掛了,需要及時監控並修改配置,否則可能會影響應用使用。
圖片

修改 config 默認 writeConcern 爲 w:1 雖然能解決寫入 hang 問題,但無法解決底層分片內存壓力以及性能下降問題等問題。

6.1.10 修改分片中宕機的實例節點信息來驗證寫入
shard2:PRIMARY> cfg.members[1].priority=0
0
shard2:PRIMARY> cfg.members[1].votes=0
0
shard2:PRIMARY> rs.reconfig(cfg)

6.1.11 再次驗證 mongos 插入數據

備註:經過修改後,不管是否指定 writeConcern 還是指定 majority 都可以。

注意應:用此時指定 w:2 就有問題。
mongos> db.testDefaultWriteConcern.insert({_id:9})
WriteResult({ "nInserted" : 1 })
mongos>db.testDefaultWriteConcern.insert({_id:10},{writeConcern:{w:"majority",wtimeout:1000}})
WriteResult({ "nInserted" : 1 })

總結:至此完成分析 PSA 架構包括集羣下使用 PSA 分片存在問題以及對應方案不管怎麼應對都需要注意潛在的影響。例如當單個數據節點宕機或者長延遲時可,以通過程序定時檢測節點狀態出,現異常時臨時將優先級別與投票設置0來避免5.0分片集羣下默認多節點寫入導致 hang 或者客戶端指定多節點寫入 hang 問題由,此帶來一致性問題與高可用性問題需要關注的。

‍關於作者:徐靖

MongoDB 中文社區成員,數據庫工程師,具有豐富的數據庫運維經驗,精通數據庫性能優化及故障診斷,目前專注於 MongoDB 數據庫運維與技術支持,同時也是公衆號《 DB 說》維護者,喜歡研究與分享數據庫相關技術。希望能夠爲社區貢獻一份力量。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章