阿里面試題:線上接口變慢,如何排查
文轉載自微信公眾號「菜鳥飛呀飛」,作者劉進坤 。轉載本文請聯系菜鳥飛呀飛公眾號。
前言
這是面試阿里時的一道題,也是平時工作中處理線上問題經常遇到的一類問題,所以耐心看完吧,無論是工作還是面試,幫助都很大。
首先線上接口變慢,原因可能有很多,有可能是網絡,有可能是慢 SQL,有可能是服務本身出現了問題,還有可能是機器達到了性能瓶頸。而機器性能瓶頸也又可以分為磁盤 IO 瓶頸、CPU 性能瓶頸、網卡瓶頸等等,本文主要從 CPU 出現性能瓶頸這個角度來分析下問題出現在哪兒。
為什么選 CPU 來分析呢?這是因為這通常是我們排查問題的第一步,自下而上排查,而通過 CPU 往往能幫助我們分析出問題出現在哪兒。
top
首先使用 top 命令,可以看到機器有多少個 CPU,以及 CPU 過去 1 分鐘、5 分鐘、15 分鐘的平均負載,各個 CPU 的使用率等信息。今天主要分析下 CPU 的負載和使用率的區別,這兩個指標經常容易搞混。
CPU 的負載(load)和使用率(utilization)
首先,需要明確的是,CPU 的負載(load)和使用率這兩個指標并不是一碼事。第一,它們的計算公式不一樣,也就是說它們所代表的含義也就不一樣;第二,這兩個指標如果異常,我們排查問題的方向也就不同。
負載的計算公式
load = (D+R)/SUM(ALL)
D 和 R 是什么意思呢?
一臺 linux 機器在運行時會有很有很多進程,這些進程按照狀態可以細分為 7 類:
- D(TASK_UNINTERRUPTIBLE)狀態:不可中斷的睡眠狀態,處于睡眠狀態,但是不可以被中斷。一般由 IO 等待引起(磁盤 IO,網絡 IO、外設 IO 等),出現非常短暫,一般很難用 ps 或者 top 等工具捕捉到,sleep 狀態下的進程不會占用 CPU 資源。
- R(TASK_RUNNING)狀態:可執行狀態,這種進程處于 CPU 的可執行隊列中,正在運行或者等待 CPU 運行。
- S(TASK_INTERRUPTIBLE)狀態:可中斷的睡眠狀態,不同于 D,該狀態下的進程也是休眠狀態,但是可以被中斷。這種進程一般在等待某種事件的發生,例如:socket 連接、信號量等。一但這些時間啊完成,進程就會被喚醒,如果不是在高負載時期,大部分進程都處于 S 狀態,不占用 CPU 資源。
- T(TASK_STOPPED)狀態:暫停狀態,進程處于停止運行狀態。
- t(TASK_TRACED)狀態:跟蹤狀態。
- Z(EXIT_ZOMBIE)狀態:僵死態,這種進程實際上已經結束運行了,只不過父進程還沒有回收它的資源(比如進程的描述符、PID 等),僵死進程會釋放掉除了進程入口之外的所有資源。
- X(EXIT_DEAD)狀態:死亡態
「因此 D+R 表示的是機器的整體負載,即 CPU 負載(正在運行著的進程)+Disk 負載+網絡負載+其他外設負載,當出現負載高時,那么問題可能不僅僅出現在 R 態的進程,還有可能是磁盤和網絡 IO 引起的。」
CPU 的使用率
CPU 是分時間片運行的,每個時間片會分配給一個進程(或者線程)。
CPU 的使用率 = CPU 執行非系統空閑進程的時間 / CPU 總的執行時間
CPU 總的執行時間又可以細分為四大類:
1.用戶進程使用時間(User Time)
- i. us(user time):用戶態進程占用的 CPU 時間
- ii. ni(nice time):改變過優先級的用戶態進程占用的 CPU 時間
2.系統內核使用時間(System Time)
- a) sys(System time):系統內核進程占用的 CPU 時間
- b) si(softirq time):軟中斷占用的 CPU 時間
- c) hi(hard irq time):硬中斷占用的 CPU 時間
3.被搶占的時間(Steal Time)
- a) st(steal time) 發生搶占,被強制中斷發生的等待時間
4.空閑時間(Idle Time)
- a) id(idle time):除磁盤 IO 等待時間以外其它等待時間
- b) wa(waiting time):等待磁盤 IO 的時間
因此,當 CPU 的使用率較高的時候,問題可能出現在用戶進程,也可能出現在系統內核,究竟是誰導致的,可以通過機器表現出來的現象或者其他的命令工具去排查(看下文)。
負載高使用率低
這種情況是通常是由于 D 狀態(不可中斷的睡眠狀態)的進程數過多導致的,即 CPU 使用率不高,但是 IO 負載很高。因此需要進一步定位是磁盤 IO 導致的,還是網絡 IO 導致的。
如何查看磁盤 IO 相關的信息呢?
可以使用命令 「iostat -x 2 5」 去查看,還可以使用 iotop 和 pidstat 查看進程的 IO 情況。
負載高且使用率高
負載高且使用率高這種情況產生的原因比較多,大概可以分為下面三大類.
1.sys 高。這個時候需要進一步查看上下文的切換(可以通過 vmstat 命令)
i. 如果是非自愿上下文切換,這種情況可能是由于 cpu 資源搶占比較激烈,由于時間片已到的原因,被系統強制調度,進而發生上下文的切換。
ii. 如果是自愿上下文切換,這種情況可能存在大量 IO 操作或者內存等系統資源存在瓶頸,大量進程無法獲取到系統資源,導致上下文切換。可以使用 iostat 查看 IO 情況后者 free 查看內存情況
2.si 高,需要進一步查看軟中斷的類型。中斷相關的信息存儲在/proc/softirqs (軟中斷)、/proc/interrupts(硬中斷),可以通過 cat 命令查看。si 高通常可能是網絡 IO 或者線程調度引起的,其中軟中斷又可以分為兩大類:
a). NET_TX 和 NET_RX NET_TX 指的是發送網絡包的軟中斷,NET_RX 指的是接收網絡包的軟中斷。如果這兩者較高,那么就是系統的網絡 IO 存在瓶頸的可能性較大
b). SCHED 進程調度或者負載均衡引起的中斷,這種中斷出現較多時,通常伴隨進程的上下文切換,一般與非自愿上下文切換一同出現,可能存在 CPU 瓶頸
3..us 高,這表示用戶進程占用 CPU 較高。可能出現的問題是:
代碼出現死循環,此時表現為多核 CPU 均出現 us 較高。(top 和 jstack 定位具體線程的堆棧)
程序屬于 CPU 密集計算型,此時表現為多核 CPU 出現 us 較高
內存問題,出現 Full GC,通常只有單核 CPU 的 us 較高(jstat -util pid)
資源等待造成線程池滿,引發 CPU 使用率飆高,通常伴隨線程池異常出現(查看應用日志)
如果是代碼死循環問題,這種問題比較簡單,通常的定位手段為:先使用 top 查詢出占用 CPU 使用率最高的進程 ID(pid),然后使用 「top -H -p pid」 命令查詢出 pid 這個進程中占用 CPU 資源最高的線程 id,此時查詢出來的線程 id 為十進制,還需要將十進制的線程 id 轉換為 16 進制,可以使用如下命令:「printf "0x%x" 線程 id」,接著使用「jstack pid 線程 id」 查看該線程的堆棧信息了,根據堆棧信息就可以快速定位代碼中哪兒出現了死循環。
如果是 GC 問題,可以使用「jstat -util pid 1000」 命令,每隔 1 秒中打印一下 JVM 中新老年代各個區域的使用情況,系統多久發生一次 Full GC,每次 GC 花費的時間。