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

MongoDB中文社群 發表於 2022-06-21
MongoDB

一. 背景

最近 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 、專家交流以及自己實驗來驗證)。

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


2.2 為什麼會造成主庫記憶體壓力?


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


    注意: 這裡只是 允許而已(資料庫提供的能力,3.2版本才開始支援)。


    客戶端是否需要根據實際 readConcern 級別,不管我們是否需要這種 majorityReadConcern 級別,資料庫 WT 引擎已經維護這些資訊在記憶體中(預設是開啟)。


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


  3. 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 緩解記憶體壓力


  1. 避免一個資料節點例項當機情況下對系統的影響, 通過完善的監控及時發現節點異常(當機、延遲),及時處理故障,否則無能為力。


  2. 禁用 MajorityReadConcern ( PSA 架構來避免記憶體壓力,同時注意 changeStream ,4.2版本不管這個引數,對於出現問題的叢集或者副本集。


  3. 臨時將異常從庫的優先順序別與投票都設定為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 dbsPOCDB    POCDB    0.000GB admin    0.000GB config   0.001GBlocal   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。

      [[email protected] data]$ ls -lh WiredTigerLAS.wt mongo mongo 28G 5月  12 16:35 WiredTigerLAS.wt[[email protected] 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(); 0PRIMARY:[db]POCDB>db.POCCOLL.find({"_id" : { "w" : 3, "i" : 34238463 }}).count(); 1


        5.4 檢視記憶體壓力情況


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

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


        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 程式。

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


            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 1151559686397 inserts per second on average0 keyqueries per second on average0 updates per second on average0 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 引數,來緩解記憶體壓力但無法解決其他問題,需要重啟例項下一次才生效。

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


              結論: 通過禁用 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=00PRIMARY:[db]POCDB> cfg.members[1].votes=00PRIMARY:[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分鐘,相對來說比重啟影響小很多。

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


                        • 重啟例項


                        結論: 重啟很快並沒有把從從例項當機後所有 OPLOG 都應用一遍,提示 No oplog entries to apply 。

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


                        總結: 至此完成分析 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 副本集發現與官方描述一致,但並沒有驗證分片架構,導致存在偏差。

                          解決方案| MongoDB 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].stateStrPRIMARYshard2:PRIMARY> cfg.members[1].stateStr(not reachable/healthy)shard2:PRIMARY> cfg.members[2].stateStrARBITERshard2:PRIMARY> use testswitched 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 xiaoxuswitched to db xiaoxumongos> 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"})


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


                                          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"} 。

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


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


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


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

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


                                              修改 config 預設 writeConcern 為 w:1 雖然能解決寫入 hang 問題,但無法解決底層分片記憶體壓力以及效能下降問題等問題。


                                              6.1.10 修改分片中當機的例項節點資訊來驗證寫入

                                                shard2:PRIMARY> cfg.members[1].priority=00shard2:PRIMARY> cfg.members[1].votes=00shard2: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 說》維護者,喜歡研究與分享資料庫相關技術。希望能夠為社群貢獻一份力量。


                                                  來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69961190/viewspace-2901820/,如需轉載,請註明出處,否則將追究法律責任。