本文根據楊亞洲老師在〖deeplus直播第258期〗線上分享演講內容整理而成。(文末有獲取回放的方式,不要錯過)
楊亞洲
OPPO MongoDB負責人
負責OPPO數萬億級資料量文件資料庫MongoDB核心研發、效能最佳化及運維工作,一直專注於分散式快取、高效能服務端、資料庫、中介軟體等相關研發。本文主要分享內容如下:
MongoDB在OPPO網際網路推廣經驗分享-如何把一個淘汰邊緣的資料庫逐步變為公司主流資料庫;談談當前國內對MongoDB誤解(丟資料、不安全、難維護)?MongoDB跨機房多活方案-實現成本、效能、一致性"三豐收";MongoDB執行緒模型瓶頸及其最佳化方法;並行遷移:MongoDB核心擴容遷移速率數倍/數十倍提升最佳化實踐;百萬級高併發讀寫/千億級資料量MongoDB叢集效能數倍提升最佳化實踐;萬億級資料量MongoDB叢集效能數十倍提升最佳化實踐;磁碟800%節省-記某服務介面千億級資料遷移MongoDB,近百臺SSD伺服器節省原理;展望:藉助MongoDB完善的分散式、高可用、機房多活等功能,如何實現NoSQL、NewSQL融合;其他-那些年我們踩過的坑。一、如何把MongoDB從淘汰邊緣變為公司主流資料庫?
背景
入職前多個大資料量業務使用MongoDB,使用中經常超時抖動;多個核心業務忍受不了抖動的痛苦,準備遷移回MySQL;MongoDB口碑差,新業務想用不敢用;入職1個月,業務遷移MongoDB到公司其他資料庫,MongoDB總叢集數減少15%。我做了啥?
從服務層最佳化、儲存引擎最佳化、部署方式最佳化等方面入手,逐一解決抖業務抖動問題;總結各個叢集抖動原因及最佳化方法,公司內部分享;收集公司所有MongoDB叢集對應使用者,成立MongoDB使用者群。入職2月後,MongoDB公司內部狀態
之前準備遷移到MySQL的幾個核心業務繼續使用MongoDB;對應業務負責人開始考慮把其他大資料量叢集遷移到MongoDB;越來越多的未使用過MongoDB的部門開始使用MongoDB。入職1年後,MongoDB相關資料增長
總叢集數增長比例:> 700%總資料量增長比例:> 2000%讀寫流量增長比例:> 550%MongoDB使用者群使用者數增長比例:> 800%總結
MongoDB贏得使用者信任原因總結:口碑二、當前國內對MongoDB誤解(丟資料、不安全、難維護)?
業務接入過程中經常諮詢的幾個問題
誤解一:丟資料;誤解二:不安全,網上一堆說MongoDB被駭客攻擊,截圖一堆新聞;誤解三:DBA吐槽MongoDB太難維護。誤解原因
MongoDB本身很優秀,但是很多DBA和相應開發把控不住;國內系統性分析MongoDB核心實現原理相關資料欠缺;網路社會以訛傳訛,DBA或者相關開發自身把控不住演變為MongoDB的鍋。三、MongoDB機房多活方案-實現成本、效能、一致性"三豐收"
社群MongoDB雙向同步方案(放棄該方案)
放棄該方案原因:
資料兩份、叢集兩份、物理成本高。三機房、五機房等更多機房多活,成本及複雜性更高;存在一致性問題,兩地叢集資料不一致,balance情況下尤為突出;由於人力原因,如果開源同步工具出現問題把控不在。方案一:同城三機房多活方案(1mongod+1mongod+1mongod方式)
每個機房代理至少部署2個,保證業務訪問代理高可用;如果某機房異常,並且該機房節點為主節點,藉助MongoDB天然的高可用機制,其他機房2個mongod例項會自動選舉一個新節點為主節點;客戶端配置nearest就近訪問,保證讀走本機房節點;弊端:如果是異地機房,B機房和C機房寫存在跨機房寫場景。如果A B C為同城機房,則沒用該弊端,同城機房時延可以忽略。方案二:同城兩機房多活方案(2mongod+2mongod+1arbiter模式)
每個機房代理至少部署2個,保證業務訪問代理高可用;如果機房A掛掉,則會在B機房mongod中重新選舉一個新的主節點。arbiter選舉節點不消耗資源;客戶端配置nearest引數,保證讀走本機房節點;弊端:如果是異地機房,B機房和C機房寫存在跨機房寫場景。如果A B 為同城機房,則沒用該弊端,同城機房時延可以忽略。方案三:異地三機房多活方案(1mongod+1mongod+1mongod方式)-解決跨機房寫
每個機房代理透過打標籤的方式,代理轉發資料到主節點在本機房的分片上去;A機房資料透過標籤識別轉發到分片shard-1,B機房資料轉發到分片shard-2,C機房資料轉發到分片shard-3。四、MongoDB執行緒模型瓶頸及其最佳化方法
MongoDB預設執行緒模型(一個連結一個執行緒)
說明:
listener執行緒負責接受所有的客戶端連結;listener執行緒每接收到一個新的客戶端連結就建立一個執行緒,該執行緒只負責處理該連結請求處理。該網路執行緒模型缺陷:
一個連結建立一個執行緒,如果10萬個連結,那麼就需要10萬個執行緒,系統負責、記憶體消耗也會很多;當連結關閉的時候,執行緒銷燬,頻繁的執行緒建立和消耗進一步增加系統負載。典型案例:
mysql預設方式、MongoDB同步執行緒模型配置,適用於請求處理比較耗時的場景,如資料庫服務;MongoDB預設執行緒模型(動態執行緒模型:單佇列方式)。
說明:
該模型把一次請求轉換為多個任務:MongoDB資料讀操作(網路IO)、db層資料訪問(磁碟IO);任務入隊到全域性佇列,執行緒池中的執行緒從佇列中獲取任務執行;同一個請求訪問被拆分為多個任務,大部分情況下透過遞迴呼叫同一個請求的多個任務會由同一個執行緒處理;當任務太多,系統壓力大的時候,執行緒池中執行緒數動態增加;當任務減少,系統壓力減少的時候,執行緒池中執行緒數動態減少。該網路執行緒模型缺陷:
執行緒池獲取任務執行,有全域性鎖競爭,這裡就會成為系統瓶頸。典型案例:
MongoDB動態adaptive執行緒模型,適用於請求處理比較耗時的場景,如資料庫服務。MongoDB最佳化後執行緒模型(動態執行緒模型-多佇列方式)
說明:
把一個全域性佇列拆分為多個佇列,任務入隊的時候按照session連結hash雜湊到各自的佇列,工作執行緒獲取獲取任務的時候,同理透過同樣的hash演算法去對應的佇列獲取任務,透過這種方式減少鎖競爭,同時提升整體效能。典型案例:
MongoDB核心多佇列adaptive執行緒模型最佳化,特定場景效能有很好的提升,適用於請求處理比較耗時的場景,如資料庫服務。五、並行遷移-叢集擴容速率N倍提升最佳化實踐
並行遷移-叢集擴容速率N倍提升最佳化實踐(高版本)
1)並行遷移過程(假設需要遷移的表名為:test, 從3節點擴容到6節點):
選取需要遷移的塊,假設源叢集有M分片,擴容新增N分片,則一般情況需要遷移的塊=min(M,N);遷移步驟:1. configServer-master選出需要遷移的塊;
2. config.locks表中id=test這條記錄上鎖;
3.通知需要遷移的源分片開始遷移;
4. 遷移完成後延時10s,重複1-4步驟實現持續性chunk資料遷移。
2)並行遷移步驟:
說明:假設需要遷移的表名為test, 源分片數M,擴容後新增分片數N
configServer-master選出需要遷移的塊,一般S=min(M, N),也就是M和N中的最小值;config.locks表中獲取id=test這條記錄對應的分散式鎖;非同步通知需要遷移的S個源分片開始遷移;等待S個chunk遷移完成;遷移完成後延時10秒;重複步驟1-5。3)並行遷移瓶頸:
獲取分散式鎖時間太長,原因:config.locks表中id=test表的分散式鎖可能被其他操作鎖住;configServer非同步通知源分片中的S個分片同時開始遷移資料到目的分片,任一個chunk遷移慢會拖累整個遷移過程;本批次確認遷移完成後,還需要延時10s;一般SSD伺服器,一個chunk遷移都在幾百ms內完成。4)最佳化方法:
避免其他操作佔用分散式鎖,例如splite我們可以關閉autoSplite功能,或者調大chunksize;configServer並行遷移不把多個分片的並行遷移放到同一個邏輯,而是放到各自的邏輯;延時放到各自分片遷移邏輯裡面控制,不受全域性延時控制;分片延時可配置,支援實時動態命令列調整。六、效能最佳化案例
案例1.千億級資料量MongoDB叢集效能數倍提升最佳化實踐-背景
1)業務背景:
核心元資料;資料量千億級;前期寫多讀少,後期讀多寫少;高峰期讀寫流量百萬級;時延敏感;資料增長快,不定期擴容;同城多活叢集。2)最佳化策略1:部署及使用方式最佳化
預分片,寫入負載均衡;WriteConcern:{ w: "majority"},寫大部分節點成功才返回客戶端OK;讀寫分離,讀從優先;enableMajorityReadConcern關閉,有效能損耗。3)最佳化策略2:儲存引擎cache淘汰策略最佳化
wiredtiger儲存引擎cache淘汰策略相關的幾個配置如下:
wiredtiger儲存引擎cache淘汰策略最佳化後配置:
eviction_target: 75%,eviction_trigger:97%,eviction_dirty_target: %3,eviction_dirty_trigger:25%,evict.threads_min:4,evict.threads_max:16
總體思想:evict執行緒儘早淘汰髒頁page到磁碟,增加evict淘汰執行緒數加快髒資料淘汰,避免使用者請求執行緒進行髒資料淘汰。
4)最佳化策略3:儲存引擎checkpoint最佳化
儲存引擎checkpoint檢測點,把當前儲存引擎髒資料全部記錄到磁碟。觸發條件如下:
固定週期做一次checkpoint快照,預設60s;增量journal日誌達到2G。少部分例項存在如下現象:一會兒磁碟IO幾乎空閒0%,一會兒磁碟IO短暫性100%。進行如下最佳化後可以緩解該問題:
checkpoint=(wait=30,log_size=1GB)
該最佳化總體思路:縮短checkpoint週期,減少checkpoint期間積壓的髒資料,緩解磁碟IO高問題。
遺留問題:SSD盤只有極少數節點有該問題,原因未知,後續繼續跟蹤。
瓶頸點:
代理快取所有客戶端的連結資訊到記憶體中,並定期更新到config庫的system.sessions表中;大流量大資料量叢集客戶端連結眾多,大量更新sessions表,最終主分片效能下降引起整個叢集效能瞬間數倍下降。最佳化方法:
5)最佳化策略4:sharding叢集system.session最佳化
該最佳化總體思路:
之前代理集中式更新單個分片,最佳化為雜湊到不同時間點更新多個分片;該最佳化後system.sessions表更新引起的瞬間效能數倍降低和大量慢日誌問題得到了解決。6)最佳化策略5:tcmalloc記憶體最佳化
db.serverStatus().tcmalloc監控發現部分mongod例項pageheap、記憶體碎片等消耗過高。透過系統呼叫分析得出:記憶體碎片率、pageheap過高,會引起分配記憶體過程變慢,引起叢集效能嚴重下降。該最佳化總體思路:
藉助gperftools三方庫中tcmalloc記憶體管理模組,實時動態調整tcmalloc記憶體Release Rate,儘早釋放記憶體,避免儲存引擎獲取cache過程阻塞變慢。
案例2.萬億級資料量MongoDB叢集效能數倍提升最佳化實踐
1)業務背景:
叢集儲存離線資料;叢集總資料量萬億級;前期主要為資料寫入,要求萬億級資料幾周內儘快全部寫入叢集;後期主要是讀流量,單次查詢資料條數比較多,要求快速返回;每隔一定時間週期(周為單位)會有持續性大量寫入。2)最佳化策略1:基礎性最佳化
分享主題六中讀寫分離、預分片、wiredtiger儲存引擎最佳化、session最佳化、tcmalloc使用最佳化等基礎性最佳化策略同樣適用於該叢集,具體詳見《分享主題六:百萬級高併發讀寫/千億級資料量MongoDB叢集效能數倍提升最佳化實踐》
3)最佳化策略2:
儲存模型最佳化前狀況
最佳化前資料模型結構如下:
1.{
2. "_id": ObjectId("5fh2ebd18856960dbac31abc"),
3. "characteristic": "xxxx",
4. "key1": "***",
5. ......
6. "keyn": "***",
7.}
以上為單條資料的資料模型,該叢集總資料量萬億級;數十萬條資料擁有同樣的characteristic特性,總特性數總計數百萬個;一次性查詢數十個characteristic很慢。瓶頸點:一次性查詢數十個characteristic特徵條件的資料,每個特徵擁有數百萬資料,一次查詢總計千萬行資料。由於資料量很大,每行資料幾乎全在磁碟,一次查詢需要千萬次IO操作,查詢成為瓶頸。
第一輪資料儲存模型最佳化:
1.{
2. "_id": ObjectId("5f29ebd18856960dbac31abc"),
3. "characteristic": "xxxx"
4. "group": [
5. {
6. "key1": "***"
7. ......
8. "keyn": "***"
9. }, #該characteristic下第一條資料
10. ......
11. {
12. "key1": "***"
13. ......
14. "keyn": "***"
15. } #該characteristic下第n條資料
16. ]
17.}
該資料模型把相同characteristic特性的數十萬資料合併到為一條資料,減少磁碟IO操作,整個讀效能會有近百倍提升。
瓶頸點:該輪最佳化解決了讀瓶頸,卻引入了新的寫瓶頸。
透過$addToSet方式向group陣列中去重追加資料,資料長度越來越長,磁碟IO壓力越來越大、寫效能成為新的瓶頸。第二輪資料儲存模型最佳化:
1.{
2. "_id": ObjectId("5f29ebd18856960dbac31abc"),
3. "characteristic": "xxxx"
4. "group": [
5. {
6. "key1": "***"
7. ......
8. "keyn": "***"
9. }, #該characteristic下第一條資料
10. ......
11. {
12. "key1": "***"
13. ......
14. "keyn": "***"
15. } #該characteristic下第n條資料
16. ]
17.}
1.{
2. "_id": ObjectId("5f29ebd18856960dbac31abc"),
3. "characteristic": "xxxx",
4. "hashNum": num,
5. "group": [
6. {
7. "key1": "***",
8. ......
9. "keyn": "***",
10. }, #該characteristic下第一條資料
11. ......
12. {
13. "key1": "***",
14. ......
15. "keyn": "***",
16. } #該characteristic下第n條資料
17. ]
18.}
如上,把同一個characteristic特徵的數十萬/數百萬資料雜湊為500份,這樣合併後group陣列中也就只包含數百條資料資訊,這樣合併後單條資料過大、MongoDB單條資料64M限制問題、磁碟IO過高等瓶頸問題都可以得到解決。
總體資料模型最佳化思路:透過合理的資料合併操作來減少網路IO、磁碟IO、MongoDB核心處理時間,最終使讀和寫達到平衡。
七、成本節省-記某服務千億級資料遷移MongoDB,百臺SSD伺服器節省最佳化實踐
成本節省-記某服務千億級資料遷移MongoDB,百臺SSD伺服器節省最佳化實踐
1)遷移背景:
需要遷移的資料量數千億級;源叢集磁碟緊張,業務寫入快,需要快速完成資料遷移;源叢集資料儲存於高io ssd伺服器;業務對效能沒太高要求;目的MongoDB叢集採用低io 大容量sata盤。2)遷移難點:如何快速完成資料遷移?
3)瓶頸點:
由於目的叢集為低io大容量sata盤,遷移太慢,源叢集磁碟有寫滿風險。4)最佳化策略:
同步資料到大容量SSD中轉叢集;複製中轉叢集資料到目標大容量SATA盤伺服器;載入資料。5)成本節省:
MongoDB預設的snappy壓縮演算法壓縮比約為2.2-3.5倍;zlib壓縮演算法壓縮比約為4.5-7.5倍(本次遷移採用zlib高壓縮演算法)。6)千億級資料遷移MongoDB收益:
源叢集磁碟消耗:目的叢集磁碟消耗 = 8:1(即使目的mongo叢集也用SSD伺服器,成本也可以節省七倍);源叢集物理資源:百臺SSD伺服器;目的MongoDB叢集資源消耗:6臺SATA盤伺服器。八、展望-如何實現MongoDB與SQL融合
問題背景
隨著MongoDB-4.2版本中對分散式事務的支援,以及MongoDB-4.4版本產品規劃路線圖可以看出,MongoDB除了保持nosql特性外,還在朝著newSql方向前行。但是在實際業務接入中發現以下現象:
開發習慣了SQL,轉MongoDB語法各種不習慣;運營和資料分析崗位人員只會寫SQL,不會mongo語句。我們能做什麼?
mongos代理增加MongoDB協議和SQL轉換支援,用最小開發成本滿足業務SQL需求;5%-10%左右的SQL協議支援,滿足90%的使用者需求。九、其他-那些年我們踩過的坑
實際業務接入MongoDB資料庫過程中,我們踩過很多坑,包括業務不合理使用、不合理運維、叢集不合理配置、MongoDB核心踩坑、誤操作等,甚至出現過同一個核心業務幾次抖動。
本次分享中叢集最佳化只列舉了主要的最佳化過程,實際最佳化過程比本次分享內容更加複雜,叢集更多最佳化細節及數十例典型踩坑過程將在今後逐步分享出來。
踩坑不可怕,在踩坑過程中學習,學習過程中減少踩坑。
十、2021規劃
國內真正擁有企業級分散式資料庫自研能力的公司主要集中在阿里、騰訊頭部幾家,即使二三線網際網路公司也無法做到真正意義上的企業級分散式資料庫研發能力,擁抱開源是一個明智的選擇。
MongoDB擁有天然的高可用、分散式擴縮容、機房多活容災、完善的負載均衡及一致性策略等功能,可以做到最少人力成本滿足業務快速增長的需求,個人認為MongoDB絕對是國內網際網路企業對分散式資料庫需求的一個值得信賴的選擇。
↓點這裡可回看本期直播