“冷靜思考,遇事不慌。有問題不可怕,解決它就好了。”
本文通過引用企業中實際發生的問題,記錄問題定位、過程排查以及方案解決的完整經過。
發現問題
某天,日常檢查服務運作情況時,發現服務日志不列印,服務狀态卻正常,為了不影響測試人員測試,遂聯系運維人員重新開機,之後就好了,奇怪。
過了幾天,再次發現服務日志不列印,檢視K8S Pod狀态,顯示重新開機了好多次了,服務也處于卡停的狀态,top一下,CPU和記憶體居高不下,之後就是再次重新開機,如此循環。。
此時的我意識到,這是個問題,不能再一味的重新開機了,得去找到根本原因解決它,不然到了線上還得了?(此時的我一點都不慌,這可比增删改查有意思多了~)
排查定位
既然CPU和記憶體都飙高,那就一個一個挨着排查吧,先查CPU。
- CPU飙高排查
1、首先 top 檢視CPU占用情況,拿到程序PID
2、在CPU高的時候,輸出該程序的所有線程的堆棧資訊
jstack -l [pid] > jstack.txt
3、分析jstack.txt檔案
發現存在大量的locked <0x360347c8> (com.mysql.jdbc.util.ReadAheadInputStream) 資訊,如下圖所示:
jstack
此時,原因好像已經顯而易見了,大多數線程都卡在了資料庫的讀取上,而這時,又一個 資料庫連接配接失敗 的異常提示将矛頭指向了資料庫。
SQL逾時告警
這時候,有同僚反應,管理端的相關頁面打不開了,一直轉圈圈,肯定是同一個問題,走吧,找DBA。
跟DBA說明了情況和連接配接的資料庫後,經查,資料庫(從庫)一切正常,各項名額正常,也沒有耗時長的查詢什麼的,奇怪。
繼續找原因。。
後來,在DBA一通kill processlist的程序後,服務突然好了,系統頁面也能正常通路了。
為了解決心中的疑惑,在我不斷的追問下,終于真相大白,原來資料庫用的是主備模式,服務配置的資料庫連接配接是SLB的連接配接,在阿裡雲指向的卻是從庫的位址,是以從一開始查詢從庫就查錯了資料庫,實際上是主庫存在長時間的查詢,導緻部分表被鎖,在被kill之後就恢複正常了。
唉,一聲歎息,道盡那幾個小時的無奈~
- 記憶體飙高排查
CPU高的問題解決了,但是服務每次重新開機後,記憶體就噌噌噌的往上竄,直到記憶體占滿重新開機,接下來該排查記憶體高的問題了:
1、top檢視記憶體占用高的程序,拿到程序PID:top
2、檢視該程序下CPU、記憶體都比較高的線程,拿到線程PID:
top -Hp [PID]
3、将線程PID轉換成十六進制
printf "%x\n" [線程PID]
4、分析jstack.txt 檔案
在上邊排查CPU時導出的 jstack.txt 檔案中查找步驟3得到的線程id轉換為十六進制後的值,發現這些線程都是GC線程,說明JVM在頻繁的GC
jstack
5、輸出堆記憶體資訊
jmap -dump:format=b,file=dump.phrof [pid]
6、使用MAT分析dump檔案
發現80%多的記憶體都被jdbc的結果集占用了,說明此時正在從資料庫拉取大量的資料到記憶體。
dump
此時,根據上圖的分析顯示,已經可以确定:代碼中從資料庫查詢了大量的資料,一次性存放到記憶體,導緻記憶體快速被占用,達到服務設定的最大記憶體後,就會被重新開機。
解決優化
到了現在,CPU和記憶體飙高的根本原因已經查明,但是這個事情已經結束了嗎,并沒有。最終問題的矛頭指向了業務代碼,根據MAT顯示的結果集資料,定位代碼,因為資料量很大,是以就需要花費更多的精力去優化,在實作功能的基礎上,還要讓系統服務平穩的運作下去。
真的是,痛并快樂着。。
如有幫助,煩請點贊、關注。有任何問題請私信、評論。