系統優化總結——系統層面

王亞普發表於2018-07-30

系統優化總結

之前組內一位大佬分享了一些關於系統效能優化方面的乾貨,這裡我將它整理成文並且加入自己平時常用的一些工具和技巧。由於關於系統效能優化涉及的內容非常多,我會分幾篇文章來分享。這次分享下定位系統層面問題的常用方法。

系統效能定義

  • Throughout 吞吐量 (系統每秒鐘可以處理的請求數)
  • Latency 延遲 (系統處理一個請求的延遲)
  • Usage 資源利用率

吞吐量和延遲的關係

  • 吞吐量越高,延遲會越大。因為請求量過大,系統太繁忙,所以響應時間會降低。
  • 延遲越小,能支援的吞吐量會越高。因為延遲短說明處理速度快,就可以處理更多的請求。
  • 非同步化可以提高系統的吞吐量的靈活性,但是不會獲得更快的響應時間。

系統效能壓測的常用工具

tcpdump

1. 常用引數:

-i:指定需要的網
-s:抓取資料包時預設抓取長度為68位元組,加上-s 0後可以抓到完整的資料包
-w:監聽的資料包寫入指定的檔案
複製程式碼

2. 示例

tcpdump -i eth1 host 10.1.1.1  // 抓取所有經過eth1,目的或源地址是10.1.1.1的網路資料包 

tcpdump -i eth1 src host 10.1.1.1  // 源地址

tcpdump -i eth1 dst host 10.1.1.1  // 目的地址
複製程式碼

如果想使用wireshark分析tcpdump的包,需要加上是 -s 引數:

tcpdump -i eth0 tcp and port 80 -s 0 -w traffic.pcap
複製程式碼

tcpcopy——線上引流壓測

tcpcopy是一種請求複製工具,用於實時和離線回放,它可以將線上流量拷貝到測試機器,實時模擬線上的真實環境,達到程式不上線的情況下承擔線上真實流量的測試。實戰演習的必備工具。

a. tcpdump錄製pace檔案

tcpdump -i eth0 -w online.pcap tcp and port 80
複製程式碼

b. 流量回放

tcpcopy -x 80-10.1.x.x:80 -i traffic.pcap
	
tcpcopy -x 80-10.1.x.x:80 -a 2 -i traffic.pcap  // 離線回放加速2倍
複製程式碼

c. 引流模式

tcpcopy -x 80-10.1.x.x:80 -r 20  // 20%引流
	
tcpcopy -x 80-10.1.x.x:80 -n 3  // 放大三倍引流
複製程式碼

wrk & ApacheBench & Jmeter & webbench

個人非常推薦wrk,輕量且壓測結果準確,結合Lua指令碼可以支援更復雜的測試場景。

壓測示例:4個執行緒來模擬1000個併發連線,整個測試持續30秒,連線超時30秒,列印出請求的延遲統計資訊。

> wrk -t4 -c1000 -d30s -T30s --latency http://www.baidu.com

Running 30s test @ http://www.baidu.com
  4 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.71s     3.19s   26.51s    89.38%
    Req/Sec    15.83     10.59    60.00     66.32%
  Latency Distribution
     50%  434.52ms
     75%    1.70s
     90%    5.66s
     99%   14.38s
  1572 requests in 30.09s, 26.36MB read
Requests/sec:     52.24
Transfer/sec:      0.88MB
複製程式碼

更多引數幫助資訊:

> wrk --help

Usage: wrk <options> <url>
  Options:
    -c, --connections <N>  Connections to keep open
    -d, --duration    <T>  Duration of test
    -t, --threads     <N>  Number of threads to use

    -s, --script      <S>  Load Lua script file
    -H, --header      <H>  Add header to request
        --latency          Print latency statistics
        --timeout     <T>  Socket/request timeout
    -v, --version          Print version details

  Numeric arguments may include a SI unit (1k, 1M, 1G)
  Time arguments may include a time unit (2s, 2m, 2h)
複製程式碼

定位效能瓶頸

可以從以下幾個方面衡量系統的效能:

  • 應用層面
  • 系統層面
  • JVM層面
  • Profiler

應用層面

應用層面的效能指標:

  • QPS
  • 響應時間,95、99線等。
  • 成功率

系統層面

系統層面指標有Cpu、記憶體、磁碟、網路等,推薦用一個犀利的命令查詢系統效能情況:

dstat -lcdngy

系統優化總結——系統層面

dstat非常強大,可以實時的監控cpu、磁碟、網路、IO、記憶體等使用情況。

  • 安裝方法

yum install -y dstat

  • 功能說明
-c:顯示CPU系統佔用,使用者佔用,空閒,等待,中斷,軟體中斷等資訊。
-C:當有多個CPU時候,此引數可按需分別顯示cpu狀態,例:-C 0,1 是顯示cpu0和cpu1的資訊。 
-d:顯示磁碟讀寫資料大小。 -D hda,total:include hda and total。 
-n:顯示網路狀態。 -N eth1,total:有多塊網路卡時,指定要顯示的網路卡。 
-l:顯示系統負載情況。 
-m:顯示記憶體使用情況。 
-g:顯示頁面使用情況。 
-p:顯示程式狀態。 
-s:顯示交換分割槽使用情況。 
-S:類似D/N。 
-r:I/O請求情況。 
-y:系統狀態。 
--ipc:顯示ipc訊息佇列,訊號等資訊。 
--socket:用來顯示tcp udp埠狀態。 
-a:此為預設選項,等同於-cdngy。 
-v:等同於 -pmgdsc -D total。 
--output 檔案:此選項也比較有用,可以把狀態資訊以csv的格式重定向到指定的檔案中,以便日後檢視。例:dstat --output /root/dstat.csv & 此時讓程式默默的在後臺執行並把結果輸出到/root/dstat.csv檔案中。	
複製程式碼

Cpu

  • 使用率:Cpu是最重要的資源,如果CPU在等待,也會導致Cpu高使用率。

    CPU利用率 = 1 - 程式佔用cpu時間/程式總的執行時間

  • 使用者時間/核心時間:大致判斷應用是計算密集型還是IO密集型。

    CPU花在使用者態程式碼的時間稱為使用者時間,而執行核心態程式碼的時間稱為核心時間。核心時間主要包括系統呼叫,核心執行緒和中斷的時間。當在整個系統範圍內進行測量時,使用者時間和核心時間之比揭示了執行的負載型別。計算密集型應用會把大量時間花在使用者態程式碼上,使用者時間/核心時間之比接近99/1。這樣的例子有影象處理,資料分析等。I/O密集型應用的系統呼叫頻率較高,通過執行核心程式碼進行I/O操作。一個進行網路I/O的Web伺服器的使用者/核心時間比大約為70/30。

  • 負載load:在特定時間間隔內執行佇列中的平均程式數。每個CPU都有一個執行佇列,佇列裡存放著已經就緒,等待被CPU執行的執行緒。理想狀態下,希望負載平均值小於等於Cpu核數。

系統優化總結——系統層面

Cpu使用率和load的區別:

  • 負載均值用來估量CPU利用率的發展趨勢,而不是某一時刻的狀況。
  • 負載均值包括所有CPU的需求,而不僅僅是在測量時活躍的。

磁碟

磁碟空間:沒有空間會導致程式無法啟動或者報錯。

du -sh //檢視當前資料夾下所有檔案大小

df -hl //以磁碟分割槽為單位檢視檔案系統
複製程式碼

有時候linux伺服器的系統日誌檔案過大導致磁碟使用率過高,推薦兩種清理方式:

sudo /dev/null > /var/log/**.log  //刪除指定的較大日誌檔案,速度快
sudo find /var/log/ -type f -mtime +30 -exec rm -f {} \  //刪除30天之前的日誌檔案
複製程式碼

磁碟許可權:沒有許可權會導致程式無法啟動或者報錯。

ll /yourdir
複製程式碼

磁碟效能測試

dd if=/dev/zero of=output.file bs=10M count=1
複製程式碼

io吞吐、iowait

這裡重點說下這兩個因素,大量的磁碟讀寫以及過高的iowait往往意味著磁碟可能是瓶頸。實際上iowait並不能反映磁碟成為效能瓶頸,它實際測量的是cpu的時間:

%iowait = (cpu idle time)/(all cpu time)
複製程式碼

所以唯一定位磁碟成為效能瓶頸的直接方法還是看read/write時間。下面我們著重介紹下如何定位io問題。

a. 巨集觀確定是否是io的問題:top命令,可以從Cpu這一行看出浪費在I/O Wait上的CPU百分比;數值越高代表越多的CPU資源在等待I/O許可權。

系統優化總結——系統層面

b. 確定具體磁碟問題:iostat

系統優化總結——系統層面

%util直觀地反應可哪一塊磁碟正在被寫入,反應了裝置的繁忙程度。每毫秒讀寫請求(rrqm/s wrqm/s)以及每秒讀寫(r/s w/s)對排查問題也提供了很多有用的資訊。

c. 確定具體程式:簡單粗暴的iotop直觀地反映了哪些程式是導致io問題的罪魁禍首。

系統優化總結——系統層面

d. ps判斷程式是否等待IO一樣強大

眾所周知,ps命令為我們提供了記憶體、cpu以及程式狀態等資訊,根據程式狀態可以很容易查到正在等待IO的程式資訊。

這裡簡單說下linux程式的幾種狀態:

  • R (TASK_RUNNING),可執行狀態。
  • S (TASK_INTERRUPTIBLE),可中斷的睡眠狀態。
  • D (TASK_UNINTERRUPTIBLE),不可中斷的睡眠狀態。
  • T (TASK_STOPPED or TASK_TRACED),暫停狀態或跟蹤狀態。
  • Z (TASK_DEAD – EXIT_ZOMBIE),退出狀態,程式成為殭屍程式。
  • X (TASK_DEAD – EXIT_DEAD),退出狀態,程式即將被銷燬。

其中等待I/O的程式狀態一般是"uninterruptible sleep"即D狀態,D狀態以及R狀態程式算為執行佇列之中,所以D狀態程式過多也會導致系統load偏高,有興趣可以看下linux load的計算原理。

檢視D狀態程式:

> for x in `seq 1 1 10`; do ps -eo state,pid,cmd | grep "^D"; echo "--------"; sleep 5; done

D 13389 /usr/libexec/gcc/x86_64-redhat-linux/4.4.7/cc1 -quiet -I../../include/cat -I../ -I. -dD message_sender.c -quiet -dumpbase message_sender.c -mtune=generic -auxbase message_sender -ggdb3 -O2 -O0 -o /tmp/ccivsNPE.s
複製程式碼

根據proc偽檔案系統獲取io相關資訊:

> cat /proc/pid/io

rchar: 548875497
wchar: 270446556
syscr: 452342
syscw: 143986
read_bytes: 253100032
write_bytes: 24645632
cancelled_write_bytes: 3801088
複製程式碼

e. 確定哪個檔案頻繁讀寫:lsof -p pid

系統優化總結——系統層面

網路

1. nestat

netstat -nt 檢視tcp相關連線狀態、連線數以及傳送佇列和接收佇列

系統優化總結——系統層面

關於tcp的狀態需要大家熟悉三次握手和四次揮手的過程,這裡先列出tcp的全部狀態。

客戶端:SYN_SENT、FIN_WAIT1、FIN_WAIT2、CLOSING、TIME_WAIT

服務端:LISTEN、SYN_RCVD、CLOSE_WAIT、LAST_ACK

Common:ESTABLISHED、CLOSED
複製程式碼

Tcp狀態變化圖(摘自網路):

系統優化總結——系統層面

關於tcp狀態的幾點說明:

  • 正常的連線應該是ESTABLISHED狀態,如果存在大量的SYN_SENT的連線,則需要看下防火牆規則。
  • 如果Recv-Q或者Send-Q持續有大量包存在,意味著連線存在瓶頸或者程式存在bug。
2. 一些其他常用技巧

關於netstat還有很多有用的技巧,這裡列出平時比較常用的:

netstat -nap | grep port 顯示使用該埠的所有程式id
    
netstat -nat |awk '{print $6}'|sort|uniq -c|sort -rn 查詢全部狀態並排序
    
awk '{print $1}' access.log |sort|uniq -c|sort -nr|head -10 分析access.log獲取訪問做多的top n的ip地址
    
netstat -nat | grep "10.1.1.1:8080" |awk '{print $5}'|awk -F: '{print $1}'|sort|uniq -c|sort -nr|head -20 連線某伺服器最多的top n的ip地址
    
netstat -s 如果重傳的包持續增加,那麼很大可能網路卡存在問題
複製程式碼

JVM

定位問題的殺手鐗——執行緒堆疊

1. 獲取執行緒堆疊的步驟:
ps -ef | grep java
sudo -u nobody jstack <pid> > /tmp/jstack.<pid>
複製程式碼

小技巧:jstack資訊是某個時刻的堆疊資訊,有時間僅僅一個jstack並不能分析出問題所在,可以適當多幾次jstack,然後進行對比分析。

2. 如何從執行緒堆疊中找到本地執行緒對應的id

系統優化總結——系統層面

nid=native thread id,特殊的是nid使用十六進位制標識,本地執行緒id是十進位制標識,所以通過進位制換算就可以講兩者對應起來。

16進位制和10進位制的互換:

printf %d 0x1b40
printf "0x%x" 6976
複製程式碼
3. Cpu消耗高的分析方法

a. 找出對應的java程式pid:

ps -ef | grep java
複製程式碼

b. 找出java程式中最消耗cpu的執行緒:

top -H -p <pid>
複製程式碼
  • 將找出的執行緒id轉換為16進位制
  • jstack獲取java的執行緒堆疊
  • 根據16進位制的id從執行緒堆疊中找到相關的堆疊資訊

說明:執行緒堆疊中可以看出對應執行緒執行的是Java程式碼還是Native method

找不到對應的執行緒堆疊?

  • 執行的Native method是重新建立的執行緒。
  • 程式碼bug,堆記憶體耗完,jvm不斷執行full gc。
  • jvm自身bug?。

垃圾收集的統計資訊——檢視Gc原因

jstat -gccause用於檢視垃圾收集的統計資訊,若有發生垃圾回收,還會顯示最後一次以及當前發生垃圾回收的原因,它比-gcutil會多出最後一次垃圾回收的原因以及當前正在發生的垃圾回收的原因。

jstat -gccause pid 1234
複製程式碼

轉載請註明出處,歡迎關注我的公眾號:亞普的技術輪子

亞普的技術輪子

相關文章