300條mongodb資料變更引發的血案-記某十億級核心mongodb叢集部分請求不可用故障踩坑記

y123456yzzyz發表於2021-05-14

  線上某核心mongodb 叢集資料量不大,單表資料量十億級,但是該叢集比較核心,影響公司收入流水。本文通過分享本次踩坑來分享整個故障經過,該故障為一次經典的 mongodb 分片 sharding 叢集踩坑故障,包括變更通知不到位、部署架構不到位、變更考慮不仔細等。

1.  問題背景

某核心mongodb 歷史叢集 ( 入職前就有的一個叢集 ) ,在對現在所有 mongodb 叢集進行風險梳理過程中,發現該叢集存在一些潛在的叢集抖動風險,該叢集架構及流量時延曲線如下:

如上圖所示,該分片叢集由3 個分片組成,叢集讀寫流量很低,峰值 QPS 4-6W/s ,平均時延 1ms ,每個分片採用 mongodb 複製集架構實現高可用。通過巡檢發現該叢集存在如下幾個問題:

該叢集只包含兩個使用者庫,userbucket 庫和 feeds_content 庫,兩個庫中只有 feeds_xxxxxxx.collection1 啟用了分片功能;第一個 userbucket 庫儲存叢集路由資訊,第二個 feeds_xxxxxxx 庫儲存約十億資料資訊;

由於該叢集主要是讀多寫少叢集,讀流量都是讀取feeds_xxxxxxx 庫中的資料,並且客戶端做了讀寫分離,所以幾乎大部分讀流量都在分片 1 。分片 2 和分片 3 只有少量資料。

庫表資訊如下表所示:

庫名

表名

功能說明

userbucket

whitexxx/expxxx

使用者路由資訊表,約 300 條資料。使用者訪問 feeds_xxxxxxx 庫前必須先獲取該表得使用者路由資料

 

 

feeds_xxxxxxx

feeds_xxx_pool

近億資料,未啟用分片

hardware_xxx_cost

未啟用分片,少量資料

news_xxx_profile

數億資料,未啟用分片

resource_xxx_info

數千萬資料,啟用分片

resource_xxx_info

未啟用分片,數億資料

resource_xxx_info

未啟用分片,數億資料

     上面的描述可以總結為下圖:

從上圖可以看出,分片2 和分片 3 幾乎沒起到任何作用;由於分片 3 有兩個節點為低 IO sata 盤,可能影響 userbucket 庫的讀寫,因此考慮直接 removeShard 從叢集中剔除分片 3 和分片 2

2.  操作過程

由於分片3 為低 IO 伺服器,有潛在抖動叢集抖動分享;同時分片 2 和分片 3 幾乎都是浪費的分片,因此打散直接通過如下 removeshad 命令刪除分片 3 和分片 2 資訊,騰出無用伺服器資源,如下圖所示:

步驟1 :登陸任一一個代理,假設是代理 mongos1

步驟2 :由於分片 3( 也就是 shard_8D5370B4 分片 ) userbucket 庫的主分片,因此報錯了,提示 "you need to drop or movePrimary these databases" ,意思是我們需要提前把該庫的主分片資訊遷移到其他分片。

步驟3 :通過 movePrimary 命令把 userbucket 庫的主分片從分片 3 遷移到分片 1

登陸監控列表中的其他兩個代理mongos2 mongos3 ,通過 db.adminCommand({"flushRouterConfig":1}) 強制重新整理路由資訊。

 

注意事項:由於 movePrimary 過程,其他代理不會感知到該庫的主分片變化,因此需要強制重新整理路由資訊或者重啟其他節點的 mongos ,參考如下:

3. 使用者反饋大部分請求業務請求不可用

對含有300 條資料的 userbucket 庫變更後,當我還在若無其事的處理其他叢集效能調優的時候,使用者突然很急的電話我反饋該核心叢集整個訪問不可用(注意:是整個 10 億資料的叢集不可用)。

收到電話後很突然,和業務人員詳細對接後可以基本上確定是因為這300 條資料變更引起。業務獲取這 300 條資料的時候,部分請求獲取成功,部分請求獲取失敗,說明肯定和 movePrimary 有關係。

於是,除了對監控列表中的所有代理做flushRouterConfig 強制路由重新整理外,還重啟了所有的代理,但是業務反饋,還是有部分請求獲取不到資料。比較棘手,我自己通過所有的 mongos 代理檢視 userbucket 庫下面的 300 條資料,完全可以獲取到資料。

於是懷疑是不是還有未重新整理路由的mongos 代理,於是登陸任一 mongos 代理獲取 config.mongos 表,檢視結果如下:

上面的config.mongos 表記錄了該叢集所有的代理資訊,同時記錄了這些代理和叢集最後一次 ping 通訊的詳細時間資訊。很明顯,該表中記錄的代理原不止叢集監控列表中的代理個數,比監控列表中的個數要多。

最終,把config.mongos 表中羅列的當前線上的所有代理強制通過 flushRouterConfig 重新整理路由後,業務恢復。

4. 問題總結

通過前面的分析可以得出,由於早期叢集監控中漏掉了部分代理,造成這部分代理對應的userbucket 路由資訊是 movePrimary 前的路由資訊,也就是指向了錯誤的分片,因此出現了路由不到資料的情況,如下圖所示:

為何使用者userbucket 庫對應表中資料有的成功有的失敗?

     因為部分代理在moveprimary 後,沒有強制重新整理該表路由資訊,造成部分代理路由獲取資料的時候路由錯誤。

為何該300 條資料部分路由資訊錯誤會造成整個 10 億叢集部分訪問不可用?

和業務實現邏輯有關係,因為業務在獲取這10 億條資料前首先需要獲取業務的路由資訊,剛好業務路由資訊存在了 userbucket 庫對應表中,業務在獲取資料前必須要獲取到業務的路由資訊資料,如果 userbucket 資料獲取不到,使用者就無法確定指向 feeds_xxxxxxx 資料

為何會遺漏部分代理重啟或者強制路由重新整理?

歷史原因,造成部分代理業務程式碼有配置,但是服務端叢集監控後設資料遺漏了,也就是服務端叢集監控漏掉了部分代理,這部分代理沒有監控起來。也有可能是mongos 代理擴容,但是叢集監控列表中沒有加入後設資料。

movePrimary 操作最安全的操作方法?

官方建議movePrimary 操作成功後需要強制路由重新整理或者重啟 mongos ,但是 movePrimary 操作成功和 mongos 重啟這個過程中有個中間狀態,如果中間狀態業務讀或者些該遷移的庫下面的表,還是可能路由錯誤。因此,最佳安全的 moveprimary 可以通過如下兩個方法操作:

法一: shutdown 所有代理,只留下一個代理,等該代理 moveprimary 成功後在重啟其他 mongos 代理。切記別遺漏代理,出現本文踩坑類似情況,提前檢查 config.mongos 表。

法二: 對主分片在該需要removeShard 的分片的庫中的所有表啟用分片功能,啟用分片功能後會有 chunk 資訊, mongodb 會自動遷移該分片的 chunk 到其他分片,整個過程可以保證路由資訊一致。


5.

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

相關文章