空循環導致CPU使用率很高
業務背景
業務背景就是需要將多張業務表中的數據增量同步到一張大寬表中,後臺系統基於這張大寬表開展業務,所以就開發了一個數據同步工具,由中間件采集binlog消息到kafka裏,然後我去消費,實現增量同步。
生產環境發現的現象
在程序發布到生產環境時候,機器的CPU使用率立馬被打到100%, load還在不停的上升,開始機器的配置是4核心8G內存,load可以到21,通過topc命令查看load average的三個值都大於20,說明機器非常的繁忙。
排查步驟及手段
1. 通過top命令查看到CPU使用率達到100%,但是內存卻沒有多大變化,內存變化可以通過gc日誌可以觀察的到。這時候配置文件中配置的是40個線程處理數據增量同步任務
2. 當時懷疑是不是線程數配置的多了原因導致的,所以將線程數調至為20個,但發現CPU依然是高位運行
3. 由於是生產環境,當時運維直接就建議升級服務器配置,有4核心8G內存升級到8核心8G內存。但是將程序部署到新服務器上後,新服務器的CPU使用率依然是100%,load也能達到20
4. 進一步將線程數調至為10個,CPU依然高位運行
5. 這是我就懷疑代碼出問題了,top -Hp pid命令查看是那些線程最耗CPU,這裏發現了一個奇怪的現象是:最耗CPU的線程數剛好10個,跟配置文件中配置的個數一樣
6. 使用jstack命令查看thread dump日誌,發現就是自己配置的10個線程的狀態為Runnable,表示一直在那裏運行。
排查到步驟6時,再結合第5點詭異的現象,回頭再去看代碼,在一個while(true)循環裏不停的從隊列中取數據,取到了則insert或update寬表, 取不到則立馬進入下一輪循環,中間沒有任何的sleep,導致系統會不停的調度該線程,占用CPU時間片,導致系統load飆升。
解決方法
在while(true)中如果從隊列中取不到數據則sleep(1000), 加上這行代碼之後,可以很明顯的看到服務器的load在下降,知道下降到一個很低的值,因為這臺機器的配置很高,只開了10個線程處理任務,負載當然很低。
空循環會導致CPU使用率很高
Linux系統中線程有幾種狀態:就緒狀態,運行狀態,阻塞狀態,掛起狀態,僵死狀態。 Linux進程線程調度是對就緒隊列中的線程進行時間片分配,阻塞狀態和掛起都在阻塞隊列中,只有喚醒之後才會被加入到就緒隊列中等待內核的調度。
空循環雖然什麽都沒有做,也沒有任何的阻塞條件(如sleep),進程一直處於運行狀態,即使時間片一到被切換了,但是改進程還是處於就緒狀態,等待下次調度。Linux內核調度是很復雜的,除了時間片之外還有優先級的權重,對於一直處於優先級的線程優先級會提高,這樣空循環所在的線程分配的時間片的比重就會增多,導致系統負載上升。
至於空循環是如何影響CPU使用率和負載的,可以參考這邊文章:https://www.2cto.com/kf/201601/488270.html
空循環導致CPU使用率很高