緣起
某個線上系統平時非常穩定,當大量更新數據時,系統的服務就會嚴重超時。問題存在了一段時間,基本猜測系統超時和大量寫入數據有關,但卻無法給出確切的解釋。為此已經寫了2篇文章:
問題解釋
問題的解釋很簡單:系統過載。由於系統定期更新大量數據,更新期間磁盤讀IO性能很差,導致每個服務的時延極大增加,整個系統的吞吐量大幅降低。該系統沒有采用合理的過載保護策略,導致后續的包全部超時!!!
什么情況會導致系統過載?系統處理能力 < 請求量 = 系統過載。設計系統時都需要系統的處理能力,比如每秒的處理能力、請求峰值、平均處理時延等。這3個指標可很好地估計系統最大處理能力、需要多少機器提供服務、是否需要擴容等。但評估容易只關注 請求量峰值,而忽略系統處理能力的變化。從公式上看,系統處理能力降低同樣會造成過載。
過載成因基本就2種:超過硬件系統極限能力、超出軟件系統的極限能力。超出硬件能力比較少見,重點關注軟件系統:操作系統和應用軟件。操作系統負責統一調配和管理硬件資源,應用系統通過系統調用使用機器的硬件資源,當對資源的使用超過操作系統所能承受的上限,將導致整體處理能力急劇下降。應用系統一般包括下列四種類型的瓶頸,某些應用系統可能同時包含2個及2個以上的瓶頸因素:
CPU(計算密集型)
MEMORY(內存消耗型)
LOAD(大並發型)
IO(包括兩類,磁盤IO密集型如DISK、DB;網絡IO密集型:流量、連接數等)
系統瓶頸理論上是系統中最慢的系統資源,比如上述諸多資源中的一種,在突破瓶頸閥值時系統出現拐點,性能會急劇下降(底層原因比如進程調度頻繁、網絡擁塞、隨機磁盤IO導致文件系統cache命中率下降及磁盤物理IO頻繁---機械臂移動、SWAP換入換出頻繁等等),因此識別系統各環節處理能力及其瓶頸至關重要。
問題的解決
解決該問題大概有以下幾種思路:
1)分散寫;
2)寫時不提供服務;
3)過載保護:丟棄超時的請求。
一種簡單的過載保護
linux的網絡包帶有時間戳,可通過ioctl獲取網絡包的時間戳。該時間戳表示網絡包接收時的時間戳(本機)。因此比較當前時間和網絡包的時間戳即可判斷請求是否超時,若超時,直接丟棄。這個辦法可快速清空超時的請求,避免由於處理超時請求,導致后續的請求無法被正常響應。
示例代碼:
int GetPkgTime(int sockfd, struct timeval *pkgTv) { struct timeval tv; int iRet; iRet = ioctl(sockfd, SIOCGSTAMP, &tv); if(iRet < 0) { return -1; } *pkgTv = tv; return 0; } int IsPkgTimeout(int sockfd, int ms ) { struct timeval tv; if( GetPkgTime( sockfd, &tv) != 0){ return -1; } long pastTime = PostTime(&tv); if( pastTime >= ms * 1000){ return 1; } return 0; }
相關文章
如何定位系統性能瓶頸和調優,參考: 性能調優攻略
更多的過載保護,參考:淺談過載保護