系統(tǒng)優(yōu)化總結(jié)
系統(tǒng)性能定義
吞吐量和延遲的關(guān)系
吞吐量越高,延遲會(huì)越大,。因?yàn)檎?qǐng)求量過(guò)大,系統(tǒng)太繁忙,,所以響應(yīng)時(shí)間會(huì)降低。
延遲越小,,能支持的吞吐量會(huì)越高。因?yàn)檠舆t短說(shuō)明處理速度快,,就可以處理更多的請(qǐng)求。
異步化可以提高系統(tǒng)的吞吐量的靈活性,,但是不會(huì)獲得更快的響應(yīng)時(shí)間,。
系統(tǒng)性能壓測(cè)的常用工具
tcpdump
1. 常用參數(shù):
-i:指定需要的網(wǎng)
-s:抓取數(shù)據(jù)包時(shí)默認(rèn)抓取長(zhǎng)度為68字節(jié),加上-s 0后可以抓到完整的數(shù)據(jù)包
-w:監(jiān)聽(tīng)的數(shù)據(jù)包寫(xiě)入指定的文件
2. 示例
tcpdump -i eth1 host 10.1.1.1 // 抓取所有經(jīng)過(guò)eth1,,目的或源地址是10.1.1.1的網(wǎng)絡(luò)數(shù)據(jù)包
tcpdump -i eth1 src host 10.1.1.1 // 源地址
tcpdump -i eth1 dst host 10.1.1.1 // 目的地址
如果想使用wireshark分析tcpdump的包,,需要加上是 -s 參數(shù):
tcpdump -i eth0 tcp and port 80 -s 0 -w traffic.pcap
tcpcopy——線(xiàn)上引流壓測(cè)
tcpcopy是一種請(qǐng)求復(fù)制工具,,用于實(shí)時(shí)和離線(xiàn)回放,,它可以將線(xiàn)上流量拷貝到測(cè)試機(jī)器,,實(shí)時(shí)模擬線(xiàn)上的真實(shí)環(huán)境,達(dá)到程序不上線(xiàn)的情況下承擔(dān)線(xiàn)上真實(shí)流量的測(cè)試,。實(shí)戰(zhàn)演習(xí)的必備工具,。
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 // 離線(xiàn)回放加速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
個(gè)人非常推薦wrk,輕量且壓測(cè)結(jié)果準(zhǔn)確,,結(jié)合Lua腳本可以支持更復(fù)雜的測(cè)試場(chǎng)景,。
壓測(cè)示例:4個(gè)線(xiàn)程來(lái)模擬1000個(gè)并發(fā)連接,整個(gè)測(cè)試持續(xù)30秒,,連接超時(shí)30秒,打印出請(qǐng)求的延遲統(tǒng)計(jì)信息,。
> 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
更多參數(shù)幫助信息:
> wrk --help
Usage: wrk
Options:
-c, --connections Connections to keep open
-d, --duration Duration of test
-t, --threads Number of threads to use
-s, --script Load Lua script file
-H, --header Add header to request
--latency Print latency statistics
--timeout 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)
定位性能瓶頸
可以從以下幾個(gè)方面衡量系統(tǒng)的性能:
應(yīng)用層面
系統(tǒng)層面
JVM層面
Profiler
應(yīng)用層面
應(yīng)用層面的性能指標(biāo):
系統(tǒng)層面
系統(tǒng)層面指標(biāo)有Cpu,、內(nèi)存、磁盤(pán),、網(wǎng)路等,推薦用一個(gè)犀利的命令查詢(xún)系統(tǒng)性能情況:
dstat -lcdngy
dstat非常強(qiáng)大,,可以實(shí)時(shí)的監(jiān)控cpu,、磁盤(pán)、網(wǎng)絡(luò),、IO、內(nèi)存等使用情況,。
yum install -y dstat
-c:顯示CPU系統(tǒng)占用,用戶(hù)占用,,空閑,,等待,中斷,,軟件中斷等信息,。
-C:當(dāng)有多個(gè)CPU時(shí)候,此參數(shù)可按需分別顯示cpu狀態(tài),,例:-C 0,1 是顯示cpu0和cpu1的信息。
-d:顯示磁盤(pán)讀寫(xiě)數(shù)據(jù)大小,。 -D hda,total:include hda and total。
-n:顯示網(wǎng)絡(luò)狀態(tài),。 -N eth1,total:有多塊網(wǎng)卡時(shí),,指定要顯示的網(wǎng)卡。
-l:顯示系統(tǒng)負(fù)載情況,。
-m:顯示內(nèi)存使用情況,。
-g:顯示頁(yè)面使用情況。
-p:顯示進(jìn)程狀態(tài),。
-s:顯示交換分區(qū)使用情況,。
-S:類(lèi)似D/N,。
-r:I/O請(qǐng)求情況。
-y:系統(tǒng)狀態(tài),。
--ipc:顯示ipc消息隊(duì)列,信號(hào)等信息。
--socket:用來(lái)顯示tcp udp端口狀態(tài),。
-a:此為默認(rèn)選項(xiàng),,等同于-cdngy。
-v:等同于 -pmgdsc -D total,。
--output 文件:此選項(xiàng)也比較有用,,可以把狀態(tài)信息以csv的格式重定向到指定的文件中,以便日后查看。例:dstat --output /root/dstat.csv & 此時(shí)讓程序默默的在后臺(tái)運(yùn)行并把結(jié)果輸出到/root/dstat.csv文件中,。
Cpu
使用率:Cpu是最重要的資源,如果CPU在等待,,也會(huì)導(dǎo)致Cpu高使用率。
CPU利用率 = 1 - 程序占用cpu時(shí)間/程序總的運(yùn)行時(shí)間
用戶(hù)時(shí)間/內(nèi)核時(shí)間:大致判斷應(yīng)用是計(jì)算密集型還是IO密集型,。
CPU花在用戶(hù)態(tài)代碼的時(shí)間稱(chēng)為用戶(hù)時(shí)間,而執(zhí)行內(nèi)核態(tài)代碼的時(shí)間稱(chēng)為內(nèi)核時(shí)間,。內(nèi)核時(shí)間主要包括系統(tǒng)調(diào)用,內(nèi)核線(xiàn)程和中斷的時(shí)間,。當(dāng)在整個(gè)系統(tǒng)范圍內(nèi)進(jìn)行測(cè)量時(shí),用戶(hù)時(shí)間和內(nèi)核時(shí)間之比揭示了運(yùn)行的負(fù)載類(lèi)型,。計(jì)算密集型應(yīng)用會(huì)把大量時(shí)間花在用戶(hù)態(tài)代碼上,用戶(hù)時(shí)間/內(nèi)核時(shí)間之比接近99/1,。這樣的例子有圖像處理,數(shù)據(jù)分析等,。I/O密集型應(yīng)用的系統(tǒng)調(diào)用頻率較高,通過(guò)執(zhí)行內(nèi)核代碼進(jìn)行I/O操作,。一個(gè)進(jìn)行網(wǎng)絡(luò)I/O的Web服務(wù)器的用戶(hù)/內(nèi)核時(shí)間比大約為70/30。
負(fù)載load:在特定時(shí)間間隔內(nèi)運(yùn)行隊(duì)列中的平均進(jìn)程數(shù),。每個(gè)CPU都有一個(gè)運(yùn)行隊(duì)列,,隊(duì)列里存放著已經(jīng)就緒,,等待被CPU執(zhí)行的線(xiàn)程。理想狀態(tài)下,,希望負(fù)載平均值小于等于Cpu核數(shù)。
Cpu使用率和load的區(qū)別:
負(fù)載均值用來(lái)估量CPU利用率的發(fā)展趨勢(shì),,而不是某一時(shí)刻的狀況,。
負(fù)載均值包括所有CPU的需求,而不僅僅是在測(cè)量時(shí)活躍的,。
磁盤(pán)
磁盤(pán)空間:沒(méi)有空間會(huì)導(dǎo)致程序無(wú)法啟動(dòng)或者報(bào)錯(cuò)。
du -sh //查看當(dāng)前文件夾下所有文件大小
df -hl //以磁盤(pán)分區(qū)為單位查看文件系統(tǒng)
有時(shí)候linux服務(wù)器的系統(tǒng)日志文件過(guò)大導(dǎo)致磁盤(pán)使用率過(guò)高,,推薦兩種清理方式:
sudo /dev/null > /var/log/**.log //刪除指定的較大日志文件,,速度快
sudo find /var/log/ -type f -mtime +30 -exec rm -f {} \ //刪除30天之前的日志文件
磁盤(pán)權(quán)限:沒(méi)有權(quán)限會(huì)導(dǎo)致程序無(wú)法啟動(dòng)或者報(bào)錯(cuò)。
ll /yourdir
磁盤(pán)性能測(cè)試
dd if=/dev/zero of=output.file bs=10M count=1
io吞吐,、iowait
這里重點(diǎn)說(shuō)下這兩個(gè)因素,大量的磁盤(pán)讀寫(xiě)以及過(guò)高的iowait往往意味著磁盤(pán)可能是瓶頸,。實(shí)際上iowait并不能反映磁盤(pán)成為性能瓶頸,它實(shí)際測(cè)量的是cpu的時(shí)間:
%iowait = (cpu idle time)/(all cpu time)
所以唯一定位磁盤(pán)成為性能瓶頸的直接方法還是看read/write時(shí)間
,。下面我們著重介紹下如何定位io問(wèn)題,。
a. 宏觀(guān)確定是否是io的問(wèn)題:top命令,,可以從Cpu這一行看出浪費(fèi)在I/O Wait上的CPU百分比;數(shù)值越高代表越多的CPU資源在等待I/O權(quán)限,。
b. 確定具體磁盤(pán)問(wèn)題:iostat
%util直觀(guān)地反應(yīng)可哪一塊磁盤(pán)正在被寫(xiě)入,,反應(yīng)了設(shè)備的繁忙程度。每毫秒讀寫(xiě)請(qǐng)求(rrqm/s wrqm/s)以及每秒讀寫(xiě)(r/s w/s)對(duì)排查問(wèn)題也提供了很多有用的信息,。
c. 確定具體進(jìn)程:簡(jiǎn)單粗暴的iotop直觀(guān)地反映了哪些進(jìn)程是導(dǎo)致io問(wèn)題的罪魁禍?zhǔn)住?/p>
d. ps判斷進(jìn)程是否等待IO一樣強(qiáng)大
眾所周知,,ps命令為我們提供了內(nèi)存,、cpu以及進(jìn)程狀態(tài)等信息,根據(jù)進(jìn)程狀態(tài)可以很容易查到正在等待IO的進(jìn)程信息,。
這里簡(jiǎn)單說(shuō)下linux進(jìn)程的幾種狀態(tài):
R (TASK_RUNNING),,可執(zhí)行狀態(tài)。
S (TASK_INTERRUPTIBLE),,可中斷的睡眠狀態(tài)。
D (TASK_UNINTERRUPTIBLE),,不可中斷的睡眠狀態(tài)。
T (TASK_STOPPED or TASK_TRACED),,暫停狀態(tài)或跟蹤狀態(tài)。
Z (TASK_DEAD – EXIT_ZOMBIE),,退出狀態(tài),進(jìn)程成為僵尸進(jìn)程,。
X (TASK_DEAD – EXIT_DEAD),退出狀態(tài),,進(jìn)程即將被銷(xiāo)毀。
其中等待I/O的進(jìn)程狀態(tài)一般是'uninterruptible sleep'即D狀態(tài),,D狀態(tài)以及R狀態(tài)進(jìn)程算為運(yùn)行隊(duì)列之中
,所以D狀態(tài)進(jìn)程過(guò)多也會(huì)導(dǎo)致系統(tǒng)load偏高,,有興趣可以看下linux load的計(jì)算原理。
查看D狀態(tài)進(jìn)程:
> 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
根據(jù)proc偽文件系統(tǒng)獲取io相關(guān)信息:
> cat /proc/pid/io
rchar: 548875497
wchar: 270446556
syscr: 452342
syscw: 143986
read_bytes: 253100032
write_bytes: 24645632
cancelled_write_bytes: 3801088
e. 確定哪個(gè)文件頻繁讀寫(xiě):lsof -p pid
網(wǎng)絡(luò)
1. nestat
netstat -nt 查看tcp相關(guān)連接狀態(tài),、連接數(shù)以及發(fā)送隊(duì)列和接收隊(duì)列
關(guān)于tcp的狀態(tài)需要大家熟悉三次握手和四次揮手的過(guò)程,,這里先列出tcp的全部狀態(tài),。
客戶(hù)端:SYN_SENT,、FIN_WAIT1,、FIN_WAIT2、CLOSING,、TIME_WAIT
服務(wù)端:LISTEN、SYN_RCVD,、CLOSE_WAIT,、LAST_ACK
Common:ESTABLISHED、CLOSED
Tcp狀態(tài)變化圖(摘自網(wǎng)絡(luò)):
關(guān)于tcp狀態(tài)的幾點(diǎn)說(shuō)明:
正常的連接應(yīng)該是ESTABLISHED狀態(tài),,如果存在大量的SYN_SENT的連接,,則需要看下防火墻規(guī)則。
如果Recv-Q或者Send-Q持續(xù)有大量包存在,,意味著連接存在瓶頸或者程序存在bug。
2. 一些其他常用技巧
關(guān)于netstat還有很多有用的技巧,,這里列出平時(shí)比較常用的:
netstat -nap | grep port 顯示使用該端口的所有進(jìn)程id
netstat -nat |awk '{print $6}'|sort|uniq -c|sort -rn 查詢(xún)?nèi)繝顟B(tài)并排序
awk '{print $1}' access.log |sort|uniq -c|sort -nr|head -10 分析access.log獲取訪(fǎng)問(wèn)做多的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 連接某服務(wù)器最多的top n的ip地址
netstat -s 如果重傳的包持續(xù)增加,那么很大可能網(wǎng)卡存在問(wèn)題
JVM
定位問(wèn)題的殺手锏——線(xiàn)程堆棧
1. 獲取線(xiàn)程堆棧的步驟:
ps -ef | grep java
sudo -u nobody jstack pid> > /tmp/jstack.pid>
小技巧:jstack信息是某個(gè)時(shí)刻的堆棧信息,,有時(shí)間僅僅一個(gè)jstack并不能分析出問(wèn)題所在,可以適當(dāng)多幾次jstack,,然后進(jìn)行對(duì)比分析。
2. 如何從線(xiàn)程堆棧中找到本地線(xiàn)程對(duì)應(yīng)的id
nid=native thread id,,特殊的是nid使用十六進(jìn)制標(biāo)識(shí),,本地線(xiàn)程id是十進(jìn)制標(biāo)識(shí),所以通過(guò)進(jìn)制換算就可以講兩者對(duì)應(yīng)起來(lái),。
16進(jìn)制和10進(jìn)制的互換:
printf %d 0x1b40
printf '0x%x' 6976
3. Cpu消耗高的分析方法
a. 找出對(duì)應(yīng)的java進(jìn)程pid:
ps -ef | grep java
b. 找出java進(jìn)程中最消耗cpu的線(xiàn)程:
top -H -p pid>
說(shuō)明:線(xiàn)程堆棧中可以看出對(duì)應(yīng)線(xiàn)程執(zhí)行的是Java代碼還是Native method
找不到對(duì)應(yīng)的線(xiàn)程堆棧,?
執(zhí)行的Native method是重新創(chuàng)建的線(xiàn)程,。
代碼bug,堆內(nèi)存耗完,,jvm不斷執(zhí)行full gc,。
jvm自身bug??。
垃圾收集的統(tǒng)計(jì)信息——查看Gc原因
jstat -gccause用于查看垃圾收集的統(tǒng)計(jì)信息,,若有發(fā)生垃圾回收,還會(huì)顯示最后一次以及當(dāng)前發(fā)生垃圾回收的原因,,它比-gcutil會(huì)多出最后一次垃圾回收的原因以及當(dāng)前正在發(fā)生的垃圾回收的原因。
jstat -gccause pid 1234