本文來自OPPO互聯網技術團隊,轉載請註名做者。同時歡迎關注OPPO互聯網技術團隊的公衆號:OPPO_tech,與你分享OPPO前沿互聯網技術及活動。html
前情提要:前不久OPPO互聯網技術推出《OPPO百萬級高併發MongoDB集羣性能數十倍提高優化實踐(上)》一文,廣受好評,現推出下篇以饗讀者。未讀過上篇的朋友能夠先讀上篇,瞭解問題的背景及優化方法,這樣能夠更好的瞭解和學習下篇內容。ios
線上某集羣峯值TPS超過100萬/秒左右(主要爲寫流量,讀流量很低,讀寫流量作了主從讀寫分離,讀流量走從節點,qps數百上千),峯值tps幾乎已經到達集羣上限,同時平均時延也超過100ms,隨着讀寫流量的進一步增長,時延抖動嚴重影響業務可用性。mongodb
該集羣採用MongoDB自然的分片模式架構,數據均衡的分佈於各個分片中,添加片鍵啓用分片功能後實現完美的負載均衡。集羣每一個節點流量監控以下圖所示:數據庫
從上圖能夠看出集羣流量比較大,峯值已經突破120萬/秒,其中delete過時刪除的流量不算在總流量裏面(delete由主觸發刪除,可是主上面不會顯示,只會在從節點拉取oplog的時候顯示)。若是算上主節點的delete流量,總tps超過150萬/秒。性能優化
在《OPPO百萬級高併發MongoDB集羣性能數十倍提高優化實踐(上)》中,咱們經過業務優化、MongoDB服務層配置優化、Wiredtiger存儲引擎層優化及硬件IO優化後,客戶端整體時延從幾百ms遲控制到了2-5ms左右,整體性能有了很大的提高,可是當有超大流量寫衝擊的時候,會有幾十ms時延抖動,幾個不一樣接口的時延以下圖所示:bash
在《上》中,咱們經過定位nvme ssd硬件的IO問題後,和廠商一塊兒分析後發現IO問題是由於操做系統版本不對引發,因而開始對線上的主從MongoDB實例的服務器硬件進行升級,升級後開始替換線上該集羣的實例。服務器
具體操做過程以下:架構
爲了驗證IO升級後的機器,咱們替換一個分片的從節點爲升級後的服務器(IO問題得以解決,IO能力從以前的500M/s寫入達到了近2G/s,咱們稱IO升級後的服務器爲高IO服務器,未升級的服務器爲低IO服務器),替換後經過iostat能夠看到該從節點的IO 100%問題獲得了很大程度的緩解,不會出現持續性IO跌0問題。併發
第一步上面的服務器跑了一週後,咱們肯定升級後的高IO服務器運行穩定,爲了謹慎起見,咱們雖然肯定該高IO服務器在從節點運行沒有問題,可是咱們須要進一步在主節點驗證是否文檔,因而咱們作了一次主從切換,該高IO服務器變爲主節點運行,也就是集羣中某個分片的主節點爲高IO服務器,可是從節點仍是低IO服務器。負載均衡
當高IO服務器在某個分片的主節點跑了數週後,咱們肯定高IO服務器在主節點運行正常,因而咱們得下結論:升級後的服務器運行穩定。
肯定高IO服務器沒問題後,咱們開始批量替換MongoDB實例到該服務器。爲了保險起見,畢竟只驗證了一臺高IO服務器在主從運行都沒問題,因而咱們考慮只把整個集羣的主節點替換爲高IO服務器(由於當時認爲客戶端都是用的默認配置,數據寫到主節點就會返回OK,雖然從節點IO慢,可是仍是能夠追上oplog速度的,這樣客戶端時延就會很好的獲得控制)。
爲了謹慎保險起見,經過上面的硬件替換升級過程,咱們只替換了全部分片的主節點,提後先後架構發生了變化,原有集羣硬件架構以下圖所示:
全部分片主節點硬件升級後的架構圖以下圖所示:
從上圖可知,新的集羣架構,主從節點服務器IO能力有比較大的差距。最開始咱們認爲業務方默認沒有設置WriteConncern,也就是默認寫入到Primary就向客戶端發送確認,所以不會影響業務寫入。
全部分片主節點升級爲高IO服務器後,多個業務接口的時間訪問延遲降到了平均2-4ms左右,可是在超大流量衝擊的時候,仍是有幾十ms的尖刺,我選取一個接口的時延爲例,以下圖所示:
從上圖能夠看出,特別是在大流量衝擊的時間點,尖刺越明顯。
在上一節,咱們替換了分片的全部主節點爲高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%
閥值,因而整個集羣訪問時延就會瞬間增長,緣由是一個分片的用戶線程須要刷盤,致使這個分片的訪問時延上升(實際上其餘分片的訪問時延仍是正常的),最終把總體平均時延拉上去了。
爲何普通平峯期也會有抖動?這很明顯不科學。
因而獲取出問題的主節點的一些監控信息,得出如下結論:
IO正常,IO不是瓶頸。
分析抖動的時候的系統top負載,負載正常。
該分片的TPS才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的原理及代碼實現分析文章,敬請關注。
除了經過replication.enableMajorityReadConcern=false
在配置文件中禁用ReadConcern Majority功能,咱們繼續把全部分片的從節點由以前的低IO服務器替換爲升級後的高IO服務器,升級後全部主從硬件資源性能徹底同樣,升級後集羣分片架構以下圖所示:
經過禁用功能,並統一主從服務器硬件資源後,查看有抖動的一個接口的時間延遲,以下圖所示:
從上圖能夠看出,經過MajorityReadConcern
功能而且替把全部從節點的低IO服務器作系統升級後,業務時間延遲抖動的峯值進一步下降了,從以前第2節中的峯值80ms下降到了如今的峯值40ms左右。
此外,3.1節提到的髒數據持續性突破eviction_dirty_trigger
閥值引發客戶端時延飆漲到幾千ms的問題得以完全解決。
經過前面的條優化,咱們發現有一個業務接口仍是偶爾有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%)的時候,慢日誌和時延都會增長,他們之間成正比關係。
經過軟件層面(MongoDB服務配置、業務優化、存儲引擎優化)及硬件優化(升級操做系統)後,該大流量集羣的核心接口時延從最初的平均成百上千ms下降到了如今的平均1-2ms,性能提高比較可觀,總體時延性能提高數十倍。
優化前主要接口時延:
在不增長物理機器的基礎上,通過一系列的優化措施,最終業務方主要接口時延控制到了幾ms,以下圖所示:
預告
近期咱們還將繼續分享以下主題,敬請關注:
百萬計高併發MongoDB集羣性能優化採坑記
線上典型集羣抖動、不可用等問題彙總分析
MongoDB文檔數據庫業務使用最佳案例分享
最後的最後,順便打一個廣告,OPPO互聯網運維雲存儲團隊急缺多個崗位:
若是對MongoDB內核源碼、Wiredtiger存儲引擎、RocksDB存儲引擎、數據庫機房多活、數據鏈路同步系統、中間件、數據庫等有興趣的同窗。歡迎加入OPPO你們庭,一塊兒參與OPPO百萬級高併發文檔數據庫研發。
工做地點:成都 / 深圳
郵箱:yangyazhou#oppo.com