OPPO百萬級高併發mongodb集羣性能數十倍提升優化實踐(下篇)

 mongodb內核、wiredtiger存儲引擎、rocksdb存儲引擎相關源碼分析詳見(後續持續更新):

https://github.com/y123456yz/reading-and-annotate-mongodb-3.6.1

  1. 前言

     溫馨提示:在進行本下篇優化文章閱讀前,可以提前瞭解下《百萬級高併發mongodb集羣性能數十倍提升優化實踐(上篇)》的問題背景及優化方法,這樣可以更好的瞭解和學習本篇性能優化下篇的內容。

《百萬級高併發mongodb集羣性能數十倍提升優化實踐(上篇)》地址: https://my.oschina.net/u/4087916/blog/3141909

2. 背景

     線上某集羣峯值TPS超過100萬/秒左右(主要爲寫流量,讀流量很低,讀寫流量做了主從讀寫分離,讀流量走從節點,qps數百上千),峯值tps幾乎已經到達集羣上限,同時平均時延也超過100ms,隨着讀寫流量的進一步增加,時延抖動嚴重影響業務可用性。該集羣採用mongodb天然的分片模式架構,數據均衡的分佈於各個分片中,添加片鍵啓用分片功能後實現完美的負載均衡。集羣每個節點流量監控如下圖所示:

從上圖可以看出集羣流量比較大,峯值已經突破120萬/秒,其中delete過期刪除的流量不算在總流量裏面(delete由主觸發刪除,但是主上面不會顯示,只會在從節點拉取oplog的時候顯示)。如果算上主節點的delete流量,總tps超過150萬/秒。

在《百萬級高併發mongodb集羣性能數十倍提升優化實踐(上篇)》中,我們通過業務優化、mongodb服務層配置優化、wiredtiger存儲引擎層優化及硬件IO優化後,客戶端總體時延從幾百ms遲控制到了2-5ms左右,總體性能有了很大的提升,但是當有超大流量寫衝擊的時候,會有幾十ms時延抖動,幾個不同接口的時延如下圖所示:

3. 硬件問題回顧及遺留問題

在《上篇》文章中,我們通過定位nvme ssd硬件的IO問題後,和廠商一起分析後發現IO問題是因爲操作系統版本不對引起,於是開始對線上的主從mongod實例的服務器硬件進行升級,升級後開始替換線上該集羣的實例。具體操作過程如下:

  1. 爲了驗證IO升級後的機器,我們替換一個分片的從節點爲升級後的服務器(IO問題得以解決,IO能力從之前的500M/s寫入達到了近2G/s,我們稱IO升級後的服務器爲高IO服務器,未升級的服務器爲低IO服務器),替換後通過iostat可以看到該從節點的IO 100%問題得到了很大程度的緩解,不會出現持續性IO跌0問題。
  2. 第一步上面的服務器跑了一週後,我們確定升級後的高IO服務器運行穩定,爲了謹慎起見,我們雖然確定該高IO服務器在從節點運行沒有問題,但是我們需要進一步在主節點驗證是否文檔,於是我們做了一次主從切換,該高IO服務器變爲主節點運行,也就是集羣中某個分片的主節點爲高IO服務器,但是從節點還是低IO服務器。
  3. 當高IO服務器在某個分片的主節點跑了數週後,我們確定高IO服務器在主節點運行正常,於是我們得下結論: 升級後的服務器運行穩定。
  4. 確定高IO服務器沒問題後,我們開始批量替換mongod實例到該服務器。爲了保險起見,畢竟只驗證了一臺高IO服務器在主從運行都沒問題,於是我們考慮只把整個集羣的主節點替換爲高IO服務器(因爲當時我認爲客戶端都是用的默認配置,數據寫到主節點就會返回OK,雖然從節點IO慢,但是還是可以追上oplog速度的,這樣客戶端時延就會很好的得到控制)。

    

爲了謹慎保險起見,通過上面的硬件替換升級過程,我們只替換了所有分片的主節點,提後前後架構發生了變化,原有集羣硬件架構如下圖所示:

從上圖可知,新的集羣架構,主從節點服務器IO能力有比較大的差距。最開始我認爲業務方默認沒有設置WriteConncern,也就是默認寫入到Primary就向客戶端發送確認,因此不會影響業務寫入。

所有分片主節點升級爲高IO服務器後,多個業務接口的時間訪問延遲降到了平均2-4ms左右,但是在超大流量衝擊的時候,還是有幾十ms的尖刺,我選取一個接口的時延爲例,如下圖所示:

從上圖可以看出,特別是在大流量衝擊的時間點,尖刺越明顯。

4. 主節點硬件升級後優化

4.1 readConcern配置優化

在上一節,我們替換了分片的所有主節點爲高IO服務器,從節點還是以前未升級的低IO服務器。由於業務方默認沒有設置WriteConncern,因此我認爲客戶端寫到主成功就會返回客戶端OK,即使從服務器性能差也不會影響客戶端寫主。

在升級主服務器後,我繼續優化存儲引擎把eviction_dirty_trigger:25%調整到了30%。

由於在超大流量的高併發衝擊,會從平峯期的幾十萬TPS瞬間飆升到百萬級別,而且該毛刺幾乎每天都會出現兩三次,比較容易復現。於是提前部署好mongostat監控所有實例,同時在每個服務器上用Iostat監控實時的IO狀況,同時編寫腳本實時採集db.serverstatus()、db.printSlaveReplicationInfo()、db.printReplicationInfo()等集羣重要信息。

當某個時間點監控出現毛刺後,於是開始分析mongostat,我們發現一個問題,即使在平峯期,髒數據比例也會持續增長到閥值(30%),我們知道當髒數據比例超過eviction_dirty_trigger:30%閥值,用戶線程就會進行evict淘汰,這樣用戶線程就會阻塞直到騰出內存空間,因此淘汰刷盤過程很慢。分析平峯期毛刺時間點對應的mongostat監控,發現如下情況:

 從上圖可以看出,集羣TPS才40-50萬左右的時候某個分片的主節點出現了髒數據達到eviction_dirty_trigger:30%閥值,於是整個集羣訪問時延就會瞬間增加,原因是一個分片的用戶線程需要刷盤,導致這個分片的訪問時延上升(實際上其他分片的訪問時延還是正常的),最終把整體平均時延拉上去了。

   爲什麼普通平峯期也會有抖動?這很明顯不科學。

   於是獲取出問題的主節點的一些監控信息,得出以下結論:

  1. IO正常,IO不是瓶頸。
  2. 分析抖動的時候的系統top負載,負載正常。
  3. 該分片的TPS才4萬左右,顯然沒到到分片峯值。
  4. db.printSlaveReplicationInfo()看到主從延遲較高。

當客戶端時延監控發現時間延遲尖刺後,我們發現主節點所有現象一切正常,系統負載、IO、TPS等都沒有到達瓶頸,但是有一個唯一的異常,就是主從同步延遲持續性增加,如下圖所示:

同時對應低IO服務器的從節點上面的io狀況如下圖:

從節點的IO性能一塌烏塗,這也正式主從延遲增加的根源。

 

    從上圖可以看出在時延尖刺的同樣時間點,主從延遲超大。於是懷疑時延尖刺可能和從節點拉取Oplog速度有關係,於是把整個mongostat、iostat、top、db.printSlaveReplicationInfo()、db.serverstatus()等監控持續跑了兩天,記錄下了兩天內的一些核心系統和mongo監控指標。

    兩天後,對着客戶端時延尖刺時間點分析對應監控數據,發現一個共同的現象,尖刺出現時間點和髒數據eviction_dirty_trigger超過閥值時間點一致,同時主從延遲在這個時間點都有很大的延遲。

    到這裏,我越來越懷疑問題和從節點拉取oplog速度有關。之前認爲業務方默認沒有設置WriteConncern,也就是默認寫入到Primary就向客戶端發送確認,可能應答客戶端前還有其他流程會影響寫入。於是查看mongodb-3.6的Production Notes,從中發現瞭如下信息:

Production Notes可以看出,mongodb-3.6默認啓用了 read concern "majority"功能,於是懷疑抖動可能和該功能有關。爲了避免髒讀,mongodb增加了該功能,啓用該功能後,mongodb爲了確保帶有帶有參數readConcern("Majority")的客戶端讀取到的數據確實是同步到大多數實例的數據,因此mongodb必須在內存中藉助snapshot 及主從通信來維護更多的版本信息,這就增加了wiredtiger存儲引擎對內存的需求。由於從節點是低IO服務器,很容易造成阻塞,這樣拉取oplog的速度就會跟不上進度,造成主節點消耗大量的內存來維護快照信息,這樣就會導致大量的內存消耗,最終導致髒數據瞬間劇增,很快達到eviction_dirty_trigger閥值,業務也因此抖動。

    說一個小插曲,因爲mongodb-3.6默認開啓enableMajorityReadConcern功能,我們在這個過程中出現過一次嚴重的集羣故障,業務流量有段時間突然暴漲,造成時延持續性達到幾千ms,現象如下:

該問題的根源也是因爲enableMajorityReadConcern功能引起,由於從節點嚴重落後主節點,導致主節點爲了維護各種snapshot快照,消耗大量內存,同時從節點和主節點的oplog延後,導致主節點維護了更多的內存版本,髒數據比例持續性增長,直到從節點追上oplog。由於我們的業務不需要readConcert功能,因此我們考慮禁用該功能(配置文件增加配置replication.enableMajorityReadConcern=false)。

    鑑於篇幅,enableMajorityReadConcern及主從硬件IO能力不足引起的嚴重業務故障,本篇不做詳細的分析,後期會寫一篇專門的《百萬計高併發mongodb集羣性能優化採坑記》中做分享,除了ReadConcern採坑,還有其他好幾個核心採坑點,敬請關注。

此外,後續會專門寫一篇ReadConcern的原理及代碼實現分析文章,敬請關注。

 

4.2 替換從節點服務器爲升級後的高IO服務器

除了通過replication.enableMajorityReadConcern=false在配置文件中禁用ReadConcern Majority功能,我們繼續把所有分片的從節點由之前的低IO服務器替換爲升級後的高IO服務器,升級後所有主從硬件資源性能完全一樣,升級後集羣分片架構如下圖所示:

通過禁用功能,並統一主從服務器硬件資源後,查看有抖動的一個接口的時間延遲,如下圖所示:

從上圖可以看出,通過MajorityReadConcern功能並且替把所有從節點的低IO服務器做系統升級後,業務時間延遲抖動的峯值進一步降低了,從之前第3節中的峯值80ms降低到了現在的峯值40ms左右。

此外,4.1節提到的髒數據持續性突破eviction_dirty_trigger閥值引起客戶端時延飆漲到幾千ms的問題得以徹底解決。

4.3 繼續優化調整存儲引起參數

通過前面的條優化,我們發現有一個業務接口還是偶爾有40ms時延尖刺,分析發現主要是eviction_dirty_trigger達到了我們配置的閥值,業務線程開始淘汰page cache,這樣就造成業務線程很慢,最終導致平均時延尖刺。

爲了進一步減緩時延尖刺,我們繼續在之前基礎上對存儲引擎調優,調整後配置如下:

    eviction_target: 75%

    eviction_trigger:97%

    eviction_dirty_target: %3

    eviction_dirty_trigger:30%

    evict.threads_min:12

    evict.threads_max:18

   checkpoint=(wait=20,log_size=1GB)

經過此輪的存儲引擎調優後,該業務的核心接口時延進一步好轉,時延尖刺相比之前有了進一步的改善,時延最大尖刺時間從前面的40ms降低到了30ms,同時尖刺出現的頻率明顯降低了,如下圖所示:

從此圖可以看出,在個別時間點還是有一次時延尖刺,對照該尖刺的時間點分析提前部署好的mongostat和iostat監控,得到如下信息:

從上圖可以看出,在峯值TPS百萬級別的時候,部分節點evict淘汰速率已經跟不上寫入速度,因此出現了用戶線程刷盤的情況。

但是,很奇怪的是,在這個時間點分析對應機器的系統負載、io狀況、內存狀況等,發現系統負載、比較正常,但是對應服務器IO偏高,如下圖所示:

同時分析對應服務器對應時間點的慢日誌,我們發現尖刺出現時間的的慢日誌統計如下:

分析非時延尖刺時間點,對應慢日誌統計如下:

分析兩個時間點慢日誌可以看出,慢日誌出現的條數和時間延遲尖刺出現的時間點一致,也就是IO負載很高的時候。

通過上面的分析可以看出,當IO比較高(util超過50%)的時候,慢日誌和時延都會增加,他們之間成正比關係。

5. 總結

通過軟件層面(mongodb服務配置、業務優化、存儲引擎優化)及硬件優化(升級操作系統)後,該大流量集羣的核心接口時延從最初的平均成百上千ms降低到了現在的平均1-2ms,性能提升比較可觀,整體時延性能提升數十倍。

優化前主要接口時延:

在不增加物理機器的基礎上,經過一系列的優化措施,最終業務方主要接口時延控制到了幾ms,如下圖所示:

6. 遺留問題

如4.3分析所述,當峯值tps持續性達到100萬左右的時候,有明顯的磁盤IO問題,但是IO寫入在不同時間點很不均衡,有時候在流量持續性高峯期存在如下現象(注: 下圖只是大概反應高峯期持續性寫入的IO趨勢狀況):

如上圖所示,有時候高峯期不同時間點磁盤IO不均衡,如果我們能把IO平均散列到各個不同時間點,這樣或許可以解決IO瓶頸問題。我試着通過繼續調大evict線程數來達到目的,但是當線程數超過一定值後效果不明顯。後續會持續分析wiredtiger存儲引擎代碼實現來了解整個機制,分析有時候磁盤IO嚴重分佈不均衡代碼實現原理。

7. 後續相關分享

   近期繼續分享如下主題,敬請關注:

  1. 《百萬級高併發mongodb集羣性能優化採坑記》
  2. 《線上典型集羣抖動、不可用等問題彙總分析》
  3. 《Mongodb文檔數據庫業務使用最佳案例分享》
  4. 《Mongodb-3.6網絡IO線程模型設計及代碼實現》

注意

    文章中的一些優化方法並不是一定適用於所有mongodb場景,請根據實際業務場景和硬件物理資源進行優化,而不要按部就班。

作者簡介

楊亞洲,前滴滴出行技術專家,現任Oppo文檔數據庫研發負責人,一直專注於分佈式緩存、高性能服務器、分佈式存儲、數據庫、中間件等相關研發,後續會持續分析Mongodb內核、wiredtiger存儲引擎、rocksdb存儲引擎源碼及,相關分享會發布到我的git賬號。

Github賬號地址: https://github.com/y123456yz   

郵箱:yangyazhou#oppo.com

發佈了146 篇原創文章 · 獲贊 77 · 訪問量 59萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章