這次聊聊業務中經常出現的重試現象,可能很多運維都被開發莫名其妙的艾特然后讓查一查業務中出現失敗的情況,很不巧剛接手MongoDB的運維就碰到了一個案例。
前段時間與業務開發討論過某業務服務的超時重試問題,這項業務依賴的數據庫是一直很熱門的MongoDB數據庫,這里采用了復制集的模式架構,且底層硬件采用KVM。業務開發反映數據庫實例慢,最近超時的業務較少,重試后都能正常進行。我與開發溝通了半小時后大致了解他的意思,又花了大半天的時間去溝通並解決這個問題,過程就不詳細贅述了。大概的意思就是這項短信服務平台業務實時性要求非常高,出現過一些業務超時現象,重試后仍然都能執行成功,希望我們排查下問題並對MongoDB集群進行一次優化。經i過與業務開發進一步的溝通,知曉他的需求和業務邏輯后,初步判斷問題不出現在數據庫集群在,業務邏輯並沒有真正的串行話執行,意思就是B發生的前提是A發生了,然而這里程序設計沒有去判斷這個A有沒有發生就去執行B。另一個問題是他覺得所謂的慢,通過超時現象變少,業務場景要求的實時性采用讀寫分離添加secondary節點滿足他的橫向擴容並不能有效解決他的問題,這些通過監控和服務器的CPU、IO、數據庫實例的真實負載都可以看到性能沒有到瓶頸。
最終我完善了集群的延遲監控,發現最多有1s到2s的延遲,且這個延遲反復出現,顯然這個現象並不能通過secondary節點來緩解他的超時問題,對於這種實時性非常高的業務我們可以通過采用更好的硬件去解決或者其他優化手段。對於MongoDB的寫入,我查閱了部分資料也建議他采用getlasterror的程序判斷寫入MongoDB是否成功,以及完善業務邏輯兩種措施解決。
對於getlasterror的安全寫入問題,總結如下,僅供參考閱讀:
getlasterror的最佳實踐
1、如果沒有特殊要求,最低級別也要使用WriterConcern.SAFE,即w=1。
2、對於不重要的數據,比如log日志,可以使用WriterConcern.NONE或者WriterConcern.NORMAL,即w=-1或者w=0,省去等待網絡的時間。
3、對大量的不連續的數據寫入,如果每次寫入都調用getLastError會降低性能,因為等待網絡的時間太長,這種情況下,可以每過N次調用一下getLastError。但是在Shard結構上,這種方式不一定確保之前的寫入是成功的。
4、對連續的批量寫入(batchs of write),要在批量寫入結束的時候調用getlastError,這不僅能確保最后一次寫入正確,而且也能確保所有的寫入都能到達服務器。如果連續寫入上萬條記錄而不調用getlastError,那么不能確保在同一個TCP socket里所有的寫入都成功。這在並發的情況下可能就會有問題。避免這個並發問題
5、對數據安全要求非常高的的配置:j=true,w="majority" db.runCommand({getlasterror:1,j:true,w:'majority',wtimeout:10000})
對於getlasterror是否采用,需要考慮業務的具體場景和業務邏輯設計,並不是所有的場景都能適用。以我這次遇到的案例,對於短信類的服務平台,本身對實時性要求非常高,且會調用第三方的短信服務平台,邏輯存在漏洞但是為了保證實時性,數據庫層面不建議他進行這樣的安全寫入機制,業務開發也覺得這種限制不可行。