出處: https://www.cnblogs.com/dennyzhangdd/p/11585971.html
一、引子
對於互聯網公司,線上CPU飆升的問題很常見(例如某個活動開始,流量突然飆升時),按照本文的步驟排查,基本1分鍾即可搞定!特此整理排查方法一篇,供大家參考討論提高。
二、問題復現
線上系統突然運行緩慢,CPU飆升,甚至到100%,以及Full GC次數過多,接着就是各種報警:例如接口超時報警等。此時急需快速線上排查問題。
三、問題排查
不管什么問題,既然是CPU飆升,肯定是查一下耗CPU的線程,然后看看GC。
3.1 核心排查步驟
1.執行“top”命令:查看所有進程占系統CPU的排序。極大可能排第一個的就是咱們的java進程(COMMAND列)。PID那一列就是進程號。
2.執行“top -Hp 進程號”命令:查看java進程下的所有線程占CPU的情況。
3.執行“printf "%x\n 10"命令 :后續查看線程堆棧信息展示的都是十六進制,為了找到咱們的線程堆棧信息,咱們需要把線程號轉成16進制。例如,printf "%x\n 10-》打印:a,那么在jstack中線程號就是0xa.
4.執行 “jstack 進程號 | grep 線程ID” 查找某進程下-》線程ID(jstack堆棧信息中的nid)=0xa的線程狀態。如果“"VM Thread" os_prio=0 tid=0x00007f871806e000 nid=0xa runnable”,第一個雙引號圈起來的就是線程名,如果是“VM Thread”這就是虛擬機GC回收線程了
5.執行“jstat -gcutil 進程號 統計間隔毫秒 統計次數(缺省代表一致統計)”,查看某進程GC持續變化情況,如果發現返回中FGC很大且一直增大-》確認Full GC! 也可以使用“jmap -heap 進程ID”查看一下進程的堆內從是不是要溢出了,特別是老年代內從使用情況一般是達到閾值(具體看垃圾回收器和啟動時配置的閾值)就會進程Full GC。
6.執行“jmap -dump:format=b,file=filename 進程ID”,導出某進程下內存heap輸出到文件中。可以通過eclipse的mat(Eclipse Memory Analyzer)工具查看內存中有哪些對象比較多;
3.2 原因分析
1.內存消耗過大,導致Full GC次數過多
執行步驟1-5:
- 多個線程的CPU都超過了100%,通過jstack命令可以看到這些線程主要是垃圾回收線程-》上一節步驟2
- 通過jstat命令監控GC情況,可以看到Full GC次數非常多,並且次數在不斷增加。--》上一節步驟5
確定是Full GC,接下來找到具體原因:
- 生成大量的對象,導致內存溢出-》執行步驟6,查看具體內存對象占用情況。
- 內存占用不高,但是Full GC次數還是比較多,此時可能是代碼中手動調用 System.gc()導致GC次數過多,這可以通過添加 -XX:+DisableExplicitGC來禁用JVM對顯示GC的響應。
2.代碼中有大量消耗CPU的操作,導致CPU過高,系統運行緩慢;
執行步驟1-4:在步驟4 jstack,可直接定位到代碼行。例如某些復雜算法,甚至算法BUG,無限循環遞歸等等。
3.由於鎖使用不當,導致死鎖。
執行步驟1-4: 如果有死鎖,會直接提示。關鍵字:deadlock.步驟四,會打印出業務死鎖的位置。
造成死鎖的原因:最典型的就是2個線程互相等待對方持有的鎖。
4.隨機出現大量線程訪問接口緩慢。
代碼某個位置有阻塞性的操作,導致該功能調用整體比較耗時,但出現是比較隨機的;平時消耗的CPU不多,而且占用的內存也不高。
思路:
首先找到該接口,通過壓測工具不斷加大訪問力度,大量線程將阻塞於該阻塞點。
執行步驟1-4:
"http-nio-8080-exec-4" #31 daemon prio=5 os_prio=31 tid=0x00007fd08d0fa000 nid=0x6403 waiting on condition [0x00007000033db000] java.lang.Thread.State: TIMED_WAITING (sleeping)-》期限等待 at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:340) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at com.*.user.controller.UserController.detail(UserController.java:18)-》業務代碼阻塞點
如上圖,找到業務代碼阻塞點,這里業務代碼使用了TimeUnit.sleep()方法,使線程進入了TIMED_WAITING(期限等待)狀態。
5.某個線程由於某種原因而進入WAITING狀態,此時該功能整體不可用,但是無法復現;
執行步驟1-4:jstack多查詢幾次,每次間隔30秒,對比一直停留在parking 導致的WAITING狀態的線程。例如CountDownLatch倒計時器,使得相關線程等待->AQS(AbstractQueuedSynchronizer AQS框架源碼剖析)->LockSupport.park()。
"Thread-0" #11 prio=5 os_prio=31 tid=0x00007f9de08c7000 nid=0x5603 waiting on condition [0x0000700001f89000] java.lang.Thread.State: WAITING (parking) ->無期限等待 at sun.misc.Unsafe.park(Native Method) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:304) at com.*.SyncTask.lambda$main$0(SyncTask.java:8)-》業務代碼阻塞點 at com.*.SyncTask$$Lambda$1/1791741888.run(Unknown Source) at java.lang.Thread.run(Thread.java:748)
三、總結
按照3.1節的6個步驟走下來,基本都能找到問題所在。
另一篇關於CPU的分析: MySQL 如何優化cpu消耗
- 誰在消耗cpu?
- 禍首是誰?
- 用戶
- IO等待
- 產生影響
- 如何減少CPU消耗?
- 減少等待
- 減少計算
- 減少邏輯運算量
- 減少邏輯IO量
- 減少query請求量(非數據庫本身)
- 升級cpu
誰在消耗cpu?
用戶+系統+IO等待+軟硬中斷+空閑
禍首是誰?
用戶
用戶空間CPU消耗,各種邏輯運算
正在進行大量tps
函數/排序/類型轉化/邏輯IO訪問...
用戶空間消耗大量cpu,產生的系統調用是什么?那些函數使用了cpu周期?
參考
Linux 性能優化解析
MySQL 幾種調式分析利器
IO等待
等待IO請求的完成
此時CPU實際上空閑
如vmstat中的wa 很高。但IO等待增加,wa也不一定會上升(請求I/O后等待響應,但進程從核上移開了)
產生影響
用戶和IO等待消耗了大部分cpu
吞吐量下降(tps)
查詢響應時間增加
慢查詢數增加
對mysql的並發陡增,也會產生上訴影響
如何減少CPU消耗?
減少等待
減少IO量
SQL/index,使用合適的索引減少掃描的行數(需平衡索引的正收益和維護開銷,空間換時間)
提升IO處理能力
加cache/加磁盤/SSD
減少計算
減少邏輯運算量
- 避免使用函數,將運算轉移至易擴展的應用服務器中
如substr等字符運算,dateadd/datesub等日期運算,abs等數學函數- 減少排序,利用索引取得有序數據或避免不必要排序
如union all代替 union,order by 索引字段等- 禁止類型轉換,使用合適類型並保證傳入參數類型與數據庫字段類型絕對一致
如數字用tiny/int/bigint等,必需轉換的在傳入數據庫之前在應用中轉好- 簡單類型,盡量避免復雜類型,降低由於復雜類型帶來的附加運算。更小的數據類型占用更少的磁盤、內存、cpu緩存和cpu周期
- ....
減少邏輯IO量
index,優化索引,減少不必要的表掃描
如增加索引,調整組合索引字段順序,去除選擇性很差的索引字段等等table,合理拆分,適度冗余
如將很少使用的大字段拆分到獨立表,非常頻繁的小字段冗余到“引用表”SQL,調整SQL寫法,充分利用現有索引,避免不必要的掃描,排序及其他操作
如減少復雜join,減少order by,盡量union all,避免子查詢等數據類型,夠用就好,減少不必要使用大字段
如tinyint夠用就別總是int,int夠用也別老bigint,date夠用也別總是timestamp....
減少query請求量(非數據庫本身)
- 適當緩存,降低緩存數據粒度,對靜態並被頻繁請求的數據進行適當的緩存
如用戶信息,商品信息等- 優化實現,盡量去除不必要的重復請求
如禁止同一頁面多次重復請求相同數據的問題,通過跨頁面參數傳遞減少訪問等- 合理需求,評估需求產出比,對產出比極端底下的需求合理去除
- ....
升級cpu
- 若經過減少計算和減少等待后還不能滿足需求,cpu利用率還高T_T
- 是時候拿出最后的殺手鐧了,升級cpu,是選擇更快的cpu還是更多的cpu了?**
- 低延遲(快速響應),需要更快的cpu(每個查詢只能使用一個cpu)
- 高吞吐,同時運行很多查詢語句,能從多個cpu處理查詢中收益
參考
《高性能MySQL》
《圖解性能優化》
大部分整理自《MySQL Tuning For CPU Bottleneck》