新聞中心
性能測試已經(jīng)是一個(gè)老生常談的話題了,不同的項(xiàng)目或多或少都會涉及到,但是每個(gè)人的經(jīng)驗(yàn)肯定有所不同。今天我想從以下幾個(gè)方面分享一下我認(rèn)為關(guān)于性能測試需要重視的要點(diǎn)。

一、性能測試的需求
1. 看似很明確的需求
當(dāng)問到性能測試的需求,我們的客戶有時(shí)候會回答,“我們要支持20000個(gè)用戶同時(shí)在線”,或者“每天有10000個(gè)用戶同時(shí)訪問這個(gè)網(wǎng)站”,更有甚者“我們也不知道,越多越好吧”。這種情況下我們應(yīng)該怎么辦呢?
當(dāng)用戶說“我們有10000個(gè)用戶同時(shí)訪問這個(gè)網(wǎng)站”的時(shí)候,看似我們的需求已經(jīng)很“明確”了。真的很明確了嗎?就拿這個(gè)例子來說,這10000個(gè)用戶都在干什么呢?假想下,可能有5000個(gè)用戶在瀏覽靜態(tài)頁面,2000個(gè)在創(chuàng)建表單,1000個(gè)在做查詢,還有... 真正產(chǎn)生壓力的用戶是誰呢?這恐怕不能單單從10000個(gè)用戶同時(shí)訪問這個(gè)網(wǎng)站得到結(jié)果。
之前做過一個(gè)項(xiàng)目,客戶為某機(jī)場,他們?yōu)橛脩籼峁┟赓M(fèi)的WiFi服務(wù),但是提供在WiFi的同時(shí),會在不同的頁面播放廣告。后臺有一個(gè)廣告管理系統(tǒng),可以做一些配置,根據(jù)優(yōu)先級來投放廣告。這其中涉及到一些復(fù)雜的算法,主要是投放廣告的比例。當(dāng)然機(jī)場還有很多物料服務(wù)器,主要是用來存放廣告的圖片等等。當(dāng)時(shí)用戶給的性能需求也就一句話,“我們每天大概有xxxxxx個(gè)用戶連接我們的WiFi”。那怎樣來做性能測試呢?
“多少個(gè)用戶連接WiFi”其實(shí)只是一個(gè)最表面的現(xiàn)象,在連接WiFi的同時(shí),其實(shí)系統(tǒng)做了很多事情。首先連接WiFi的時(shí)候會去訪問分發(fā)服務(wù)器,確定要播放哪幾個(gè)廣告,其次會根據(jù)分發(fā)的廣告,去物料服務(wù)器上找相應(yīng)的資源,然后打點(diǎn)把廣告訪問的歷史數(shù)據(jù)記錄到數(shù)據(jù)庫中。這是能聯(lián)想到的一些最基本的后臺操作,每一個(gè)行為都有可能成為一個(gè)瓶頸,都需要去認(rèn)真考慮。
所以在考慮性能需求的時(shí)候,一定要清楚背后的邏輯是什么,同時(shí)需要分析用戶活動(dòng),從而確認(rèn)系統(tǒng)的性能測試目標(biāo)。
2. 歷史數(shù)據(jù)
“性能現(xiàn)在的需求也不是很明確,但我們有原來的老系統(tǒng),能幫我們看下嗎?”
對于性能測試來說,如果有很詳細(xì)的歷史數(shù)據(jù),這將是一個(gè)非常珍貴的數(shù)據(jù)源,我們可以從中分析出很詳細(xì)用戶行為。之前我們做過一個(gè)項(xiàng)目,性能測試的需求基本上就是從歷史數(shù)據(jù)中得到的。我們對數(shù)據(jù)庫進(jìn)行了很詳細(xì)的分析,從而設(shè)計(jì)性能測試用例,這其中主要包含系統(tǒng)的哪些service承受了壓力、壓力是多大、用戶量是多大等等。
需要特別注意的是,如果對于業(yè)務(wù)的了解不夠深,很容易導(dǎo)致場景的設(shè)計(jì)缺失,從而導(dǎo)致測試的場景和實(shí)際的場景有所偏差。比如分析某張表,分析出系統(tǒng)有10000個(gè)插卡拔卡記錄,有的人可能直接就開始寫腳本了,模擬一個(gè)用戶每天做10000次插卡拔卡的操作。殊不知,這10000次插卡和拔卡的操作是4000個(gè)用戶完成的...而性能的瓶頸很有可能就在用戶的管理,而不是插卡拔卡產(chǎn)生的壓力。
所以建議在做歷史數(shù)據(jù)分析的時(shí)候,一定盡量把場景分析全面,并且需要去了解真正的業(yè)務(wù),才可以盡量減少分析的錯(cuò)誤。
3. 不明確的需求
“我們也不知道需求是什么,你們看著辦吧?!?/p>
這種情況通常出現(xiàn)在一個(gè)新的項(xiàng)目,客戶對上線后的用戶體量并沒有很好的認(rèn)知。我們應(yīng)該怎么做呢?
通常我們會對系統(tǒng)進(jìn)行負(fù)載測試,就是在被測系統(tǒng)上不斷增加壓力,直到性能指標(biāo)(如響應(yīng)時(shí)間)超過預(yù)期或者某種資源已經(jīng)快達(dá)到飽和狀態(tài),這個(gè)時(shí)候我們基本就可以確定系統(tǒng)的瓶頸是什么、支持多大的吞吐量。再通過一段時(shí)間的穩(wěn)定性測試,讓系統(tǒng)在一定的時(shí)間內(nèi)(比如一周)維持一定的壓力,去查看相應(yīng)的性能指標(biāo)。
這樣,我們就可以告訴我們的用戶,系統(tǒng)現(xiàn)在支持多大的用戶訪問量、吞吐量是多少。
當(dāng)然,現(xiàn)在提到的都是客戶的需求,對于性能測試本身來說,我們也有一些明確的需求,也就是我們通常提到的index,下面我會繼續(xù)深入討論這個(gè)問題。
二、定義性能測試的指標(biāo)
1. 性能測試指標(biāo)
通常來說我們會關(guān)注如下的性能測試指標(biāo)。
- 響應(yīng)時(shí)間:比較熟悉的就是2-5-8原則(據(jù)統(tǒng)計(jì)當(dāng)網(wǎng)站慢一秒就會流失十分之一的客戶),通常來說,2到5秒,頁面體驗(yàn)會比較好,5到8秒還可以接受,8秒以上基本就很難接受了。但是有的項(xiàng)目也會例外,比如從海量的數(shù)據(jù)中去查詢某些數(shù)據(jù),或者生成報(bào)告(年度報(bào)告),這種可能就不太適合2-5-8原則,但是前提是要管理好客戶的期望。
- 吞吐量:指的是在單位時(shí)間內(nèi)客戶端和服務(wù)器成功傳送數(shù)據(jù)的數(shù)量
- 并發(fā):客戶/服務(wù)端同一批用戶同時(shí)執(zhí)行一個(gè)操作的數(shù)量
- 資源使用率:通常來說,我們關(guān)注的資源就是幾大塊:內(nèi)存、CPU、I/O和網(wǎng)絡(luò)
- 成功率:比如在某些情況下,API調(diào)用的成功率
當(dāng)了解了我們所需要關(guān)注的性能指標(biāo),就可以來定義具體的性能測試指標(biāo)了。
我之前做一個(gè)比較大的項(xiàng)目的性能測試,系統(tǒng)提供很多服務(wù),有web service,有windows的services,基本上每個(gè)services都是單獨(dú)部署在一臺window的服務(wù)器上,所有的services都連到同一個(gè)數(shù)據(jù)庫。由于這個(gè)系統(tǒng)并不存在網(wǎng)頁,所以我們并沒有把頁面加載這些考慮在內(nèi)。我們從歷史數(shù)據(jù)中首先了解到了系統(tǒng)需要滿足的并發(fā)量,針對每個(gè)service去做相應(yīng)的壓力測試,并且定義對應(yīng)的性能測試指標(biāo)。如下圖:
上圖就是對于web service性能測試的指標(biāo)和結(jié)果,這個(gè)結(jié)果是我們基于每秒有7個(gè)Post請求+3個(gè)get請求而產(chǎn)生的。
這其中的性能指標(biāo)包含了我們對這個(gè)service的單獨(dú)的內(nèi)存監(jiān)控、CPU監(jiān)控,以及成功率的監(jiān)控,還有相應(yīng)的原因分析。通常來說,對于內(nèi)存、CPU等的占用率,業(yè)界都有很共識的標(biāo)準(zhǔn),大家可以拿來作為參考,由于我們服務(wù)器是8g內(nèi)存,8核CPU,所以表現(xiàn)還是相當(dāng)不錯(cuò)的。
其他的service也是類似的,我們考察的點(diǎn)主要在于CPU,內(nèi)存的占用率,還有成功率。
由于網(wǎng)絡(luò)一開始我們就認(rèn)為不是瓶頸(都在內(nèi)網(wǎng)),所以我們并沒有把網(wǎng)絡(luò)相應(yīng)的指標(biāo)加進(jìn)去。
需要特別注意的數(shù)據(jù)庫server,和其他windows service或者web service不一樣,對于DB server我們需要特別關(guān)注的是I/O,數(shù)據(jù)庫的瓶頸一般都是出現(xiàn)在I/O上面。
自定義指標(biāo)
在項(xiàng)目中我們往往還要自定義一些其他的指標(biāo),來查看某些特定的性能。比如:
為了分析方便,我們在一些service的關(guān)鍵方法上都加上時(shí)間戳,便于去做trouble shooting。于是我們有了一下的指標(biāo),圖一是某個(gè)方法的平均調(diào)用時(shí)間,圖二是我們計(jì)算出的最慢調(diào)用方法的top10:
總體來說,性能測試的指標(biāo)可以從不同的方面去定義,比如響應(yīng)時(shí)間、資源利用、并發(fā)數(shù)、吞吐量、并發(fā)數(shù)...
三、如何trouble shooting
剛才提到了性能測試中的一些相應(yīng)的指標(biāo),當(dāng)我們發(fā)現(xiàn)某些指標(biāo)不正常的時(shí)候,我們應(yīng)該怎樣去做呢?
性能測試的指標(biāo)往往又是相互關(guān)聯(lián)的,當(dāng)遇到問題時(shí),我們需要從這些指標(biāo)的結(jié)果中去尋找真正的root cause,這也是性能測試最重要的點(diǎn)之一。廢話少說,我們直接來看幾個(gè)例子吧:
1. 內(nèi)存泄露
內(nèi)存泄露是性能測試中很常見的一個(gè)點(diǎn),比如下圖,很明顯能看到有一個(gè)內(nèi)存上升的趨勢,這種情況下很可能就有內(nèi)存泄露的情況。但是也有例外,比如是C#或者Java寫的代碼,因?yàn)閮?nèi)存回收是垃圾回收器自己的行為,不像C語言那樣需要手動(dòng)回收,所以即使內(nèi)存在一段時(shí)間內(nèi)有上升的趨勢,但是只要能回到原點(diǎn),就不存在內(nèi)存泄露的現(xiàn)象。
內(nèi)存泄露很多時(shí)候并沒有很明顯的征兆,有一次客戶抱怨服務(wù)掛掉了,這個(gè)服務(wù)之前有好幾個(gè)月都運(yùn)行的好好的,怎么說掛就掛呢,然后我們對系統(tǒng)日志做了很詳細(xì)的分析,發(fā)現(xiàn)就是內(nèi)存泄露惹的禍。每天內(nèi)存泄露一點(diǎn)點(diǎn),到第三個(gè)月的時(shí)候才導(dǎo)致服務(wù)崩潰,這在短期的性能測試中就很難發(fā)現(xiàn)。
下圖是當(dāng)時(shí)我們在做性能測試的時(shí)候發(fā)現(xiàn)的問題,很明顯內(nèi)存在一段時(shí)間內(nèi)直接從200多MB升到了500多MB,很有可能存在內(nèi)存泄露的情況。怎樣trouble shoooting呢?
我們借助了第三方的工具ANTS,最終有下面的結(jié)果,很明顯Sting類變成了最大的類,高達(dá)211MB,這個(gè)是什么原因造成的呢?然后我們在工具上找了String object的調(diào)用鏈,最終發(fā)現(xiàn)代碼中有一行的判斷錯(cuò)誤,導(dǎo)致了內(nèi)存上升。
2. API響應(yīng)速度慢
這是一個(gè)很常見的問題,單單看現(xiàn)象,其實(shí)我們并不知道系統(tǒng)是哪里出的問題,有可能是網(wǎng)絡(luò)慢,有可能是內(nèi)存出現(xiàn)了瓶頸,也有可能是I/O的問題... 怎樣定位呢?
首先我們還是找到相應(yīng)的apiserver,通過性能測試的報(bào)告,我們發(fā)現(xiàn)了下面的問題,message queue隨著時(shí)間的變化增長特別快,這個(gè)時(shí)候看API server本身的內(nèi)存和CPU并沒有什么問題,那么問題到底處在哪呢?
這個(gè)時(shí)候其實(shí)很容易就聯(lián)想到是數(shù)據(jù)庫出現(xiàn)了問題,對比兩個(gè)不同的版本,發(fā)現(xiàn)我們對數(shù)據(jù)庫的存儲過程有所更改,最終發(fā)現(xiàn)是存儲過程的更改導(dǎo)致了某個(gè)索引失效,最終反映到API就是速度明顯變慢。添加索引后問題得到了解決。一般來說,當(dāng)我們在修改數(shù)據(jù)庫的時(shí)候一定要特別小心,很可能一個(gè)小的改動(dòng)就會導(dǎo)致系統(tǒng)性能的急劇下降。
對于性能測試來說,往往我們發(fā)現(xiàn)的問題都是表象上的問題,比如頁面反應(yīng)很慢。但這有可能是多種原因?qū)е碌?,需要去做深入的分析。這也要求我們在做性能測試時(shí)去收集足夠的信息,以支撐分析,同時(shí)借助一些第三方工具,才能真正定位到問題。
四、關(guān)于數(shù)據(jù)庫
其實(shí)性能測試我前面已經(jīng)提到了很多,為什么還要把數(shù)據(jù)庫單拿出來說呢?我們來看看數(shù)據(jù)庫服務(wù)器的特殊之處:
1. 關(guān)于內(nèi)存
我們在進(jìn)行服務(wù)器的內(nèi)存的測試的時(shí)候,如果是普通的APP server,當(dāng)我們對內(nèi)存進(jìn)行監(jiān)控時(shí),通常都是有一個(gè)metrics, 比如某個(gè)APP的service的內(nèi)存使用量不能超過總量的百分之多少,對于整個(gè)sever來說,通常也會有比較多的內(nèi)存剩余。
而當(dāng)我們?nèi)ゲ榭匆粋€(gè)數(shù)據(jù)庫server的時(shí)候,會發(fā)現(xiàn)可用的內(nèi)存量通常只有10M或者5M(取決于你的配置)...... 而當(dāng)你把數(shù)據(jù)庫的內(nèi)存增加一倍,可用的內(nèi)存通常還是只有10M左右,這是什么原因呢?
其實(shí)這與數(shù)據(jù)庫本身的工作原理有關(guān)系,數(shù)據(jù)庫中io操作的基本單位為頁,當(dāng)數(shù)據(jù)庫執(zhí)行一條語句,比如一條查詢語句,它會先從物理磁盤中把相應(yīng)的頁加載到內(nèi)存,然后再進(jìn)行操作。
因?yàn)閿?shù)據(jù)庫本身就在不停的讀寫,所以數(shù)據(jù)庫內(nèi)存當(dāng)中會緩存各種各樣的數(shù)據(jù),為了更快的讀寫,數(shù)據(jù)庫會有算法去維護(hù)這些內(nèi)存中的數(shù)據(jù),以保證盡可能的使得數(shù)據(jù)都是從內(nèi)存中獲得,而不是從物理內(nèi)存中得到(內(nèi)存的訪問速度是納秒級,硬盤是微秒級)。所以一般來說,數(shù)據(jù)庫會基本用光所有的內(nèi)存。
這就是為什么數(shù)據(jù)庫會吃內(nèi)存了,這與其工作原理有關(guān)系,只要能保證系統(tǒng)能正常運(yùn)行就可以了,其他的內(nèi)存都可以用來緩存數(shù)據(jù)。
2. 關(guān)于死鎖
死鎖通常是指爭搶資源不當(dāng),讓雙方因?yàn)閷Ψ秸莆樟俗约旱馁Y源而無限期的等下去。如下圖所示:
發(fā)生死鎖的原因很多,大部分是由于事務(wù)之間對資源訪問順序的交替,或者并發(fā)修改統(tǒng)一記錄導(dǎo)致的,數(shù)據(jù)庫對待死鎖有著不同的策略,對于SQL server來說,它會隨機(jī)殺掉其中的一個(gè),至少保證另外一個(gè)事務(wù)的正常運(yùn)行;而對于Oracle來說,它會對兩個(gè)事務(wù)進(jìn)行一個(gè)評估,會殺掉它認(rèn)為不那么重要的一個(gè)。
所以我們在數(shù)據(jù)層面,需要去監(jiān)控死鎖的發(fā)生情況,一般來說,死鎖是不可避免的,但是一旦死鎖發(fā)生頻率很多,必定會影響到業(yè)務(wù)。
我們可以用很多工具去監(jiān)控死鎖,比如SQL Profile。對于死鎖的修正也是對于高并發(fā)的事務(wù),盡量減少長度;把鎖的優(yōu)先級調(diào)整低一些(用低隔離級別);按同一順序訪問對象,盡量避免事務(wù)中的用戶交互。
3. 關(guān)于磁盤
因?yàn)閷τ跀?shù)據(jù)庫來說,最重要的就是讀寫的操作,磁盤對于數(shù)據(jù)庫來說是非常重要的。
現(xiàn)在好多數(shù)據(jù)庫都是直接放在云盤上的,云盤上的磁盤有著自己的結(jié)構(gòu),但是很多年前,如果是自己的服務(wù)器,我們還要考慮到磁盤陣列,來保證磁盤的效率和安全性。
對于磁盤來說,我們通常會從讀寫方面來衡量,有幾個(gè)比較通過的指標(biāo)可以用來衡量數(shù)據(jù)庫磁盤的性能,比如Average Disk queue lenth,數(shù)據(jù)生命周期等等。
另外值得一提的是數(shù)據(jù)庫的碎片整理。當(dāng)數(shù)據(jù)庫讀寫一段時(shí)間之后,由于頻繁的插入數(shù)據(jù),會導(dǎo)致數(shù)據(jù)并不是按照順序排列在磁盤上面,這樣當(dāng)我們在查相關(guān)數(shù)據(jù)的時(shí)候,磁盤往往要去不同的區(qū)域查找數(shù)據(jù),導(dǎo)致性能降低,這個(gè)時(shí)候我們很有必要去運(yùn)行磁盤的碎片整理,來保證數(shù)據(jù)庫性能。
但是這個(gè)job本身就很占磁盤的I/O,所以盡量選擇系統(tǒng)不忙的時(shí)候進(jìn)行。
4. 關(guān)于索引
相信索引是大家很熟悉的一個(gè)話題了,當(dāng)數(shù)據(jù)量很小時(shí),不建索引,進(jìn)行全表掃描的的性能尚可接受。但是數(shù)據(jù)量大時(shí),必須借助索引。所以索引的適當(dāng)與否,是性能好壞的關(guān)鍵。
比如執(zhí)行一條語句
- SELECT UserName, UserID FROM US.UserDetail WHERE UserName = “李四”
這條語句這樣執(zhí)行與創(chuàng)建的索引有關(guān)系,如果沒有索引就需要進(jìn)行全表掃描,這樣加載到內(nèi)存當(dāng)中的頁在數(shù)據(jù)量很大的情況下就相當(dāng)多了。如果建立了適當(dāng)?shù)乃饕?,可能只需要加載幾頁內(nèi)存就可以了。
當(dāng)我們用一些工具比如sql profile跟蹤到一些長的查詢的時(shí)候,我們就需要去看看索引是否建立恰當(dāng)。這在數(shù)據(jù)庫的優(yōu)化中也是很重要的一個(gè)方面。當(dāng)然,索引雖然對讀的性能有幫助,但是對寫的性能卻有影響。
我們也需要再適當(dāng)?shù)臅r(shí)候reindex索引,原因就是如果索引在物理存儲上不連續(xù),也會導(dǎo)致性能的下降,這與磁盤的碎片整理是一個(gè)道理。
一般來說,如果系統(tǒng)設(shè)計(jì)合理,最終的瓶頸都會出現(xiàn)在數(shù)據(jù)庫上,而我們在做性能測試時(shí),也需要了解數(shù)據(jù)庫的特殊之處,去更好的做性能測試。
五、關(guān)于性能測試的工具
對于市面上的那么多性能測試工具,怎樣選擇呢?
性能測試的工具實(shí)際上有很多,能列出來的比如Jmeter、Loadrunner,Galtling等等,這取決于很多方面:
- 需要測試測試服務(wù)端的性能?還是前端的性能?或者是mobile端的性能?后端的話可以選擇Jmeter、Loadrunner或者Gatling。前端的話可以選擇比如Fiddler、httpWatch等,手機(jī)端可以選用GT或者APT等。
- 項(xiàng)目的成本,很多性能測試的工具是收費(fèi)的,也有很多是開源的,需要結(jié)合具體的項(xiàng)目進(jìn)行考慮。
- 我們也可以自己寫腳本來完成性能測試,只要能滿足要求項(xiàng)目的要求就可以。但是通常來說使用已有的工具一般會產(chǎn)生比較好的測試報(bào)告,除非一些特殊情況,比如某些協(xié)議的特殊性,或者很簡單的一些場景等,一般來說還是建議使用市面上已有的工具。
- 使用工具的同時(shí),盡量保證測試系統(tǒng)和測試的腳本在同一個(gè)網(wǎng)絡(luò)里面,這樣測試出來的結(jié)果才有意義。
寫在最后
總的來說,性能測試其實(shí)是一個(gè)很復(fù)雜的過程。需要結(jié)合不同的方面正確分析需求,根據(jù)需求設(shè)計(jì)出合理的測試場景,定義出性能測試的指標(biāo),并且在測試中選擇合適的工具進(jìn)行測試,并且結(jié)合測試的多項(xiàng)結(jié)果進(jìn)行分析和trouble shooting。
【本文為專欄作者“張逸”原創(chuàng)稿件,轉(zhuǎn)載請聯(lián)系原作者】
本文題目:關(guān)于性能測試需要重視的要點(diǎn)
當(dāng)前路徑:http://fisionsoft.com.cn/article/cceogii.html


咨詢
建站咨詢
