一個系統過載的案例及其解決辦法

系統出現過載現象(或問題)的緣由和場景有不少, 這裏並不試圖概括總結; 而是如題, 就一個特定的案例, 分享一些過載保護的實踐辦法.數據庫

案例

系統R須要經過輪詢(讀取)數據庫中存儲的記錄狀態, 進行一些業務補償的操做, 然後再對數據庫的狀態進行更新.併發

輪詢機制實現並未採用定時週期的方式, 而是採用 請求 - 響應 - 再請求 的事件驅動方式.單元測試

在壓測的時候發現, 數據庫訪問(不管讀寫)大量失敗, 錯誤日誌狂刷.測試

分析

經排查, 因爲數據庫被多個系統公用, 其它系統的一些SQL執行耗時超長, 資源佔用過多, 致使系統R的數據庫訪問請求超時失敗, 而失敗後的不斷重試, 變本加厲, 最終數據庫撐不住掛了.線程

相似的典型場景, 如 秒殺 :日誌

海量併發請求下, 系統已經出現過載, 請求響應過緩, 而用戶耐不住, 則不斷嘗試刷新頁面, 寄指望於請求被響應. 可事與願違, 系統在沒有保護的狀況下, 將持續超過載的狀態, 直到 宕機.code

解決

面對過載, 可能最容易被想到的是 擴容. 是的, 它是一種不下降服務質量的必不可少的預防方案, 但咱們不得不接受一個事實:事件

系統的容量(或處理能力)始終是有限的, 即便不考慮成本的擴容, 也只能應付你能夠預見程度, 一旦有個"萬一", 系統仍將崩潰.資源

若服務能夠降級, 則 限流 是可以應對"萬一"的良方, 下面就 限流 這個思路, 分享幾個辦法.請求

定時

將輪詢改用定時來實現, 就能夠保證穩定的訪問節奏, 加之一旦支持動態修改定時週期時間, 即可以根據實際狀況靈活調整節奏了.

不只作到了 限流 , 更是作到了 流控, 不錯.

能夠實際應用中會發現, 初始的定時週期很難找到合適的值, 須要在處理實時性和請求量之間平衡.

我的不太喜歡用定時方案, 它不只讓單元測試結果不穩定, 還浪費線程.

休息

請求 - 響應 - 再請求 的事件驅動的輪詢方式, 有個好處: 就是在正常狀況下, 讓訪問節奏由數據庫說了算, 即數據庫響應快, 輪詢則快, 反之則放緩. 只惋惜異常的狀況下, 就如案例中那樣.

休息很好理解:

若人累了, 就不要繼續強撐, 讓身體休息一會(踹口氣), 再恢復工做.

同理, 當數據庫訪問失敗後, 優雅地拒絕掉隨後若干次請求, 讓數據庫休息一會.

如何作到優雅? 就本案例而言則是, 讀取失敗後的若干次請求迅速返回空集合. 總之, 拒絕能夠是除拋異常外, 任何對處理邏輯有意義的默認NULL結果.

若干次究竟是多少次? 這與選擇定時時間同樣是須要平衡的.

補償

本案例中輪詢的讀取失敗返回空集合是個好的休息辦法, 可要是狀態更新失敗呢? 無法返回結果, 只能拋異常, 但異常又會致使重試, 這不只沒讓數據庫休息, 且可能致使大量ERROR Stacktrace日誌輸出.

不拋異常, 而將異常轉換爲一次失敗記錄(日誌), 這些記錄能夠用來對數據庫狀態的不一致進行補償操做, 具體如何補償, 什麼時機開始補償, 這裏就不展開細說了, 它們都須要由業務場景來決定.


寫在最後, 上述三種辦法都不是銀彈, 也並不是互斥, 徹底能夠根據狀況結合使用的.

相關文章
相關標籤/搜索