接到現場報告,客戶MongoDB間數據延遲愈來愈大,有的已經超過2-3個小時,形成有些打到延遲mongodb上面的數據庫請求沒法反應數據庫的最新更改。這個問題反覆出如今高峯期尤爲明顯,持續近一月。算法
客戶爲異地雙機房架構,兩地機房相隔上千千米,帶寬250M,光纖,具體鏈路狀況不明mongodb
F5 | F5數據庫
N中心 | S中心windows
業務服務 ... 業務服務 | 業務服務 .... 業務服務緩存
mongodb4 ... mongodb6 <----|----- mongodb1(主) ... mongdb2服務器
全部數據庫在一個複製集共6臺(其中一臺不參與選舉),使用的是mongodb replicaSet 進行同步。網絡
S中心數據庫反覆產生延遲,其中一次rs.printSlaveReplicationInfo()查詢集羣狀況顯示以下,數據庫4,5,6三臺延遲(此處不是每次都是三臺同時延遲,有時只是這三臺中的兩臺或者一臺出現延遲,可是1,2從不延遲),延遲時間最多達到2-3個小時,非高峯期能夠恢復。架構
插入圖app
首先查看有問題的機器,發現4,5,6三臺均在N中心,1,2均在S中心,而用rs.status查看,能夠發現當延遲時,這三臺都以1爲同步源,也就是發生跨網絡同步。tcp
因爲三臺出現同步的機器都在N中心,可猜想與鏈路有關。客戶ping命令未顯示延遲,說明ping的數據量可能不夠或者問題是由某種條件觸發的。
是否有可能同步帶寬超過了鏈路限制,咱們和網絡方面的人進行了溝通,獲得的消息是咱們的應用使用的帶寬沒有達到上限。最高也就極限帶寬的2/3左右。爲確認帶寬狀況,咱們本身也對帶寬進行了分析。
南北中心業務高峯期正常狀況下帶寬佔用70Mb/s,也沒有達到理論高度,並且正常狀況下某些瞬時,帶寬能夠超過150Mb/s,也證實服務遠遠沒有達到帶寬上線。咱們進一步對產生延遲的狀況下的帶寬進行分析,發現了一個奇怪的狀況
:在產生延遲的狀況下,數據庫用來同步的帶寬遠遠不足60Mb/s ,只有不到15Mb/s。也就是說,數據庫有大量堆積,產生延遲的時候,數據庫同步反而變慢了。
同時咱們監控了S中心內未延遲節點與通中心主節點同步時的帶寬約爲17Mb/s ,因爲mongodb每臺slave都是複製一樣的數據,能夠推知其餘機器都應該按照這個速率進行同步。而延遲節點只有4.5Mb/s左右。
咱們又查看了延遲節點的監控,查看了延時節點從正常狀態轉入延遲狀態的帶寬狀況,發現其使用帶寬會在某種狀況下發生驟降
究竟是什麼致使這種情況呢?是mongodb自己的問題嗎?
這種狀況咱們仍是決定圍繞核心現象來進行分析:這幾臺服務器和正常的服務器有什麼不一樣。咱們一一排查了機器配置,軟件配置的問題,確保出問題的服務器和正常服務器沒有原則不一樣,同時也查了這幾臺服務器在延遲時的表現,發現延遲時,這幾臺mongo的指標遠遠沒有到達瓶頸,因此惟一可疑的不一樣仍是在網絡上,即便ping沒有發現問題,咱們仍是決定要進一步抓包。
由於抓包對性能有顯著影響,爲了抓出特徵包,咱們選在節點開始延遲,而且延遲時間還在增大時進行抓包,抓包時間在1分鐘之內。
抓包語句 tcpdump -i eth0 port xxx -w /home/tcpdump.cap
下面是咱們看到的狀況(使用Wireshark)
網絡中出現了大量的Dup ACK,這個說明了TCP出現了數據丟失致使了重傳。
TCP的流程是面向鏈接,會盡力保證數據的完整性,因此有失敗重傳機制,Dup ACK 就是這個機制的一部分。
每一個包都有一個Seq號和一個包長度,正常狀況下,某一端全部發送的包的Seq號是連續的,好比
第一個包的Seq是1,長度是2,
則第二個包Seq=1+2=3,長度4,
第三個包的Seq=3+4,長度若干,
以此類推,當接收端收到這個包時會將這個包放到緩衝區,緩衝區裏可能有不少從另外一端發來的包,他們必須能連續起來,接收端會向發送端發包時告知會帶上我接收到的最後一個連續的包的Seq是多少,這個數據會放在ACK裏傳送。
上面注意:
插入圖
假如丟失了一個包會怎麼樣?接收端發現緩存裏的包丟失了,然後面的包已經到了,就會觸發上面的Dup ACK,告知我接收到的連續的數據的序號到哪裏爲止,也就是DupACK包的ACK。這個請求會反覆發送,直到服務端發送缺失包(retransmit)。在收到缺失包前,後續的包即便收到也不會ack。這個是retransmit的流程。
上面的流程在dump中典型的例子用wireshark打開會是下面的樣子:注意這裏包括【TCP Previous segment not captured】【Dup ACK】 等內容都是wireshark的分析結果。TCP包中並不包含這些標籤。
首先從端發現丟包【TCP Previous segment not captured】而後引起從端大量發送dup ack,直到主端重傳經過TCP Fast Retransmition 或Retransmition 重傳missing package,見下圖。重傳期間全部有缺失的包會暫存入buffer。
在wireshark中能夠對重傳的包進行過濾 tcp.analysis.retransmission
過濾結果爲32條,佔所抓包的0.3%,這個統計能夠看作是就是丟包的統計。然而因爲這樣的丟包,觸發tcp進行反覆溝通引發的混亂遠遠大於此,若是統計全部由此引發的dup Ack, Out of Order,Fast Retransmit,Transmit,window_update 等,能夠看到這些處理丟包的請求佔到了5.5%之多。
同時剛纔提到發送端可能發送不少包,可是隻是其中一個丟了。這些沒有丟掉的包會放在一個緩衝區中,這個緩衝區叫作window,因爲後續包不斷到來,而失去的包沒有補上,因此tcp不能向上層轉發,因而就會更改window的大小,這個也是很是消耗資源的行爲。
總結來講:
從庫的dump中,共抓包16.81秒10176個包,錯誤重傳形成的dup ack和重傳包達到562 佔 5.5%;
主庫(同步源)的dump中,共抓包12秒 3501個包,錯誤重傳以及dup ack相關的包占到325,佔比9.3%;
而同機房內部節點徹底未見丟包與重傳,且在跨機房節點處於無延遲正常狀態也未見丟包重傳。
爲了確保這個現象只在跨機房調用中出現,咱們也抓取了同機房同步,以及跨機房無延遲時的狀況,這兩種狀況下均無上述問題。重傳錯誤與流量的關係可見下圖,折線爲吞吐,柱形爲error包數量,可見流量的重大轉折均伴隨有相關錯誤,下圖也展示了window scaling的狀況,展現出因爲錯誤發生,windows size 的修改狀況,可見6分鐘左右發生了一次大的scaling,正對應了dump中的錯誤。
在丟包率0.3%的狀況下,mongodb的replicaSet發生了比較嚴重的問題。表現爲同步速率大幅降低,而後產生延遲。
客戶網絡狀況比較複雜,鏈路是運營商鏈路,還有許多第三方廠商的設備,難以在短期內排查緣由,因此再發生同步時,咱們採用rs.syncFrom腳本切換同步源臨時解決這個問題:即當腳本監測延遲>10分鐘時,馬上切換到另一個數據源。
此方法基於在產生延遲後,當即切換另一個數據源能夠從新打開同步鏈路快速同步,瞬間同步時速能夠到達160Mb/s,延時數據能很快消失。
可是在執行過程當中,出現過切換同時,mongodb因爲大量寫入而影響業務的狀況,因此後來改成在非高峯先講須要修改的節點改成hide節點再進行處理。
解決網絡延遲:這個是最直接的解決辦法,畢竟同機房同步未見問題。
減小replicaSet的量:檢查業務中的寫入更新刪除邏輯,減小生成量。具體分析和處理方法將另文描述。
使用mongoshake等第三方同步工具:因爲還沒有明晰重傳爲什麼致使複製集處理速度降低,因此不使用複製集也可能能夠解決這個問題。且複製集對源的選擇有本身的算法,雖然能夠短期的切換,可是更多時候是根據mongodb自身的算法進行選擇,好比在本案中,三臺N節點Mongo都到S節點同步,形成帶寬乘以3倍,雖然能夠手動制定其中兩臺從同節點一臺複製,但mongodb仍是有可能自動根據本身的算法切換源。而使用第三方同步工具能夠解決上面的兩個問題。可是要檢查第三方工具在0.3%網絡丟包下的工做狀況。