測(cè)試環(huán)境的重要性無(wú)需多說(shuō),大家都知道測(cè)試環(huán)境要盡量的模擬生產(chǎn)環(huán)境,當(dāng)然也包括數(shù)據(jù)。這樣測(cè)試的結(jié)果才會(huì)更加準(zhǔn)確的反應(yīng)真實(shí)的性能。就連開(kāi)發(fā)過(guò)程,都已經(jīng)開(kāi)始在大數(shù)據(jù)量下加壓開(kāi)發(fā)了。那么,關(guān)于測(cè)試數(shù)據(jù),你了解多少呢?
通常說(shuō)的測(cè)試數(shù)據(jù)可以分為兩類(lèi):
一是為了測(cè)試性能而準(zhǔn)備的數(shù)據(jù),這是用來(lái)模擬“壓力”的數(shù)據(jù)。也就是常說(shuō)的數(shù)據(jù)量、歷史數(shù)據(jù)等。一般都會(huì)根據(jù)需求或者經(jīng)驗(yàn)很容易估算出來(lái),比如案件年增長(zhǎng)量為5%,去年數(shù)據(jù)量為100W,測(cè)試需要保證3年后系統(tǒng)仍可正常運(yùn)行,那么就需要計(jì)算并模擬出3年后的總數(shù)據(jù)量,在這個(gè)基礎(chǔ)上進(jìn)行測(cè)試。
二是用來(lái)輔助測(cè)試使用的數(shù)據(jù)。比如有一個(gè)對(duì)案件進(jìn)行打分的功能,只有符合一定條件的案件才會(huì)出現(xiàn)在打分列表 中。那么我們要測(cè)這個(gè)打分的操作,首先就要保證有可用的案件,這就需要去生成測(cè)試數(shù)據(jù),該數(shù)據(jù)可能一經(jīng)使用就失效了(已經(jīng)打過(guò)分就不能再打了)。這樣,每 次測(cè)試這個(gè)功能,就需要準(zhǔn)備這樣一批數(shù)據(jù)。這里的測(cè)試數(shù)據(jù),更多的是和測(cè)試流程有關(guān),是為了能夠正常的進(jìn)行測(cè)試,而不是涉及到性能的。
我們這里要說(shuō)的是第一類(lèi),對(duì)性能測(cè)試結(jié)果產(chǎn)生直接影響的數(shù)據(jù)。
先看兩個(gè)小案例,涉及到了案件表(T_AJ)和法院編號(hào)列(N_FY)、立案日期列(D_LARQ)。案件表中模擬了一百萬(wàn)測(cè)試數(shù)據(jù),測(cè)試簡(jiǎn)單的查詢(xún)操作,根據(jù)經(jīng)驗(yàn),預(yù)期響應(yīng)時(shí)間在2秒之內(nèi)。
案例1. 查詢(xún)本院案件列表,相應(yīng)的SQL如下:
select * from T_AJ
where N_FY=10
order by D_LARQ desc
執(zhí)行這個(gè)操作耗時(shí)近10s,顯然達(dá)不到正常預(yù)期。
經(jīng)排查,生成的100W測(cè)試數(shù)據(jù)中,所有的N_FY列值都為10。這樣,最明顯的問(wèn)題就是,查詢(xún)的結(jié)果集數(shù)量完全偏離了正常范圍。如果實(shí)際有 100家法院,正常分布下,每家法院只有1W的案件,但測(cè)試數(shù)據(jù)的FY只有一個(gè)值,通過(guò)這個(gè)查詢(xún),查出了原來(lái)100家法院的數(shù)據(jù)。無(wú)論是在數(shù)據(jù)庫(kù)處理中 (如本例的排序),還是在程序的處理中(如展現(xiàn)或者是對(duì)數(shù)據(jù)做進(jìn)一步處理),兩者的性能差異都是很顯著的。所以這個(gè)測(cè)試結(jié)果是無(wú)效的。
有人說(shuō),這個(gè)例子太弱了,結(jié)果集差了100倍,性能當(dāng)然不一樣了。那是不是數(shù)據(jù)總量和結(jié)果集大小都一致,測(cè)試結(jié)果就是有效了呢?
案例2. 查詢(xún)本院一個(gè)月內(nèi)收的案件,相應(yīng)SQL如下:
select * from T_AJ
where N_FY=10 and D_LARQ between '20110101' and '20110201'
這個(gè)操作,查出來(lái)的結(jié)果只有一千條數(shù)據(jù),屬于正常范圍。但查詢(xún)的時(shí)間還是超過(guò)5秒,依然超出了我們的預(yù)期。
查看數(shù)據(jù)發(fā)現(xiàn),N_FY=10的數(shù)據(jù)有近50萬(wàn),占了總數(shù)據(jù)量的一半,D_LARQ在一月份的數(shù)據(jù)也占了差不多一半。但是同時(shí)符合兩個(gè)條件的數(shù)據(jù)還是一千條左右。那么這里的問(wèn)題就不在于結(jié)果集了,而是是否能利用索引進(jìn)行查詢(xún),看如下兩個(gè)圖就能很好理解了。
在正常數(shù)據(jù)中,每家法院的數(shù)據(jù)可能占總數(shù)據(jù)量的1%,一個(gè)月時(shí)間段內(nèi)的數(shù)據(jù)可能占總數(shù)據(jù)量更少,假設(shè)是0.5%。那么這時(shí)我們通過(guò)N_FY和 D_LARQ兩個(gè)條件進(jìn)行查詢(xún),數(shù)據(jù)庫(kù)會(huì)進(jìn)行估算:符合D_LARQ查詢(xún)條件的數(shù)據(jù)大概有5000條,符合N_FY查詢(xún)條件的數(shù)據(jù)大概有1萬(wàn)條,那么用 D_LARQ上的索引進(jìn)行查詢(xún)是最快的,可以迅速的將查詢(xún)范圍縮小到5000條,然后在這5000條中去檢查N_FY是否也符合條件。
過(guò)程如圖一所示(手繪草圖^_^)。

圖一
注:數(shù)據(jù)按行存儲(chǔ),小方塊表示符合該列查詢(xún)條件的數(shù)據(jù),陰影表示符合所有查詢(xún)條件,也就是最終的結(jié)果集。箭頭線(xiàn)段表示為了完成查詢(xún),需要掃描的數(shù)據(jù)量,本圖中即符合LARQ查詢(xún)條件的數(shù)據(jù)。下同。
但在本例中不正常的數(shù)據(jù)條件下,數(shù)據(jù)庫(kù)會(huì)知道:符合N_FY查詢(xún)條件的數(shù)據(jù)有50萬(wàn)條,符合D_LARQ的也有近50萬(wàn)條,如果使用其中一列的 索引將一百萬(wàn)的范圍縮減到50萬(wàn),比從頭到尾掃描整個(gè)表做的工作還要多(為什么呢?需要了解索引的結(jié)構(gòu)和原理),那還是不要用索引了吧。于是數(shù)據(jù)庫(kù)會(huì)依次 檢查每一條數(shù)據(jù),判斷N_FY和D_LARQ是否符合條件。
如圖二所示。

圖二
注:本圖中實(shí)際掃描的數(shù)據(jù)量就是整張表的數(shù)據(jù),但結(jié)果集和圖一是一樣大的。
這樣,就可以知道,總數(shù)據(jù)量一樣,結(jié)果集大小一樣,為什么性能差了很多了。就是因?yàn)閿?shù)據(jù)分布不合理,導(dǎo)致數(shù)據(jù)庫(kù)無(wú)法正常使用索引,從而進(jìn)行了全 表掃描。當(dāng)然,這個(gè)數(shù)據(jù)分布,我們依然可以歸類(lèi)到結(jié)果集中去,那就是要保證每一個(gè)查詢(xún)條件“單獨(dú)的結(jié)果集”都要符合真實(shí)情況,而不僅僅是整個(gè)查詢(xún)最終的 “總結(jié)果集”。
看個(gè)這兩個(gè)簡(jiǎn)單的小例子,我們?cè)賮?lái)總結(jié)一下關(guān)于測(cè)試數(shù)據(jù),需要注意的內(nèi)容:
-
最根本、也是大家都知道的就是數(shù)據(jù)量,性能測(cè)試必須保證能在預(yù)期的數(shù)據(jù)量下進(jìn)行測(cè)試。在一萬(wàn)條記錄中查詢(xún),和在一百萬(wàn)數(shù)據(jù)中查詢(xún),顯然是大大不同的,可以把數(shù)據(jù)量看做一種“壓力”,這個(gè)就不用再解釋了。
但是在比較大型的系統(tǒng)中,這一點(diǎn)可能也不是很容易做好,因?yàn)檫@類(lèi)系統(tǒng)往往有著復(fù)雜的數(shù)據(jù)庫(kù),上百?gòu)埖臄?shù)據(jù)表。對(duì)每張表都進(jìn)行數(shù)據(jù)模擬顯然是不現(xiàn)實(shí) 的,也是沒(méi)有意義的,因?yàn)椴皇敲繌埍矶忌婕暗酱髷?shù)據(jù)量。那么如何選取不容易遺漏呢?通常通過(guò)兩種方式:從設(shè)計(jì)和業(yè)務(wù)角度分析表間關(guān)系、從現(xiàn)有實(shí)際數(shù)據(jù)量進(jìn) 行分析推測(cè)。
-
確保結(jié)果集在正常范圍內(nèi)。結(jié)果集的大小直接影響后續(xù)很多工作的性能,如數(shù)據(jù)排序分組、分頁(yè)、程序中的邏輯校驗(yàn)或者是展現(xiàn)。
-
數(shù)據(jù)分布必須合理,盡量接近真實(shí)。數(shù)據(jù)的分布,其實(shí)也就是數(shù)據(jù)的真實(shí)性,它直接決定了數(shù)據(jù)庫(kù)是否使用索引、選用哪個(gè)索引,也就是常說(shuō)的查詢(xún)計(jì)劃。不同的查詢(xún)計(jì)劃也就是不同的數(shù)據(jù)訪(fǎng)問(wèn)路徑,性能差別可能會(huì)很大。
這里主要涉及到的是索引的問(wèn)題,需要大家對(duì)索引的原理有一定的了解,索引如何工作、數(shù)據(jù)庫(kù)如何選擇索引、和索引有關(guān)的一寫(xiě)重要概念如區(qū)分度(selectivity)等等。
- 最好的數(shù)據(jù)來(lái)自生產(chǎn)環(huán)境。這是顯而易見(jiàn)的,使用真實(shí)的數(shù)據(jù)測(cè)出來(lái)的結(jié)果才是最準(zhǔn)確的。但是絕大多數(shù)情況下,我 們沒(méi)有這樣的好運(yùn),可能是客戶(hù)禁止、也可能是生產(chǎn)環(huán)境數(shù)據(jù)量比較小。那就只好自己想辦法來(lái)模擬了,需要注意的也就是上面說(shuō)到的幾點(diǎn)。這里再推薦一種方法, 數(shù)據(jù)翻倍。比如已經(jīng)有了真實(shí)的數(shù)據(jù)十萬(wàn)條,但我們需要一百萬(wàn)條,那就可以通過(guò)寫(xiě)一些SQL或者存儲(chǔ)過(guò)程,將現(xiàn)有的數(shù)據(jù)不斷翻倍(簡(jiǎn)單的說(shuō),復(fù)制到臨時(shí)表, 根據(jù)需要修改一些列,再插回到原表),這樣的數(shù)據(jù)真實(shí)性還是比較高的。
關(guān)于測(cè)試數(shù)據(jù),我想說(shuō)的就是以上幾點(diǎn)了。另外再補(bǔ)充上一些相關(guān)內(nèi)容,也是性能測(cè)試人員需要關(guān)注的。
-
重點(diǎn)了解IO的概念,更準(zhǔn)確的說(shuō)應(yīng)該是物理IO。一般來(lái)講,數(shù)據(jù)庫(kù)的瓶頸或者查詢(xún)的主要耗時(shí)就是IO。所以,數(shù)據(jù)庫(kù)優(yōu)化的一個(gè)重要方向就是盡量減小IO。
IO是不是只和數(shù)據(jù)量(行數(shù))有關(guān)呢?舉一個(gè)例子:
select co1, col2, col3, col4, col5 from T_AJ
where condition...
T_AJ數(shù)據(jù)量有100萬(wàn),表中有近200列,此查詢(xún)耗時(shí)大于10秒。而另一種實(shí)現(xiàn)方式,首先將col1-col5以及查詢(xún)條件中的幾個(gè)列的數(shù)據(jù)抽取到一張臨時(shí)表(#T_AJ)中。然后,
select co1, col2, col3, col4, col5
from #T_AJ where condition...
臨時(shí)表#T_AJ和原數(shù)據(jù)表有同樣的數(shù)據(jù)量(行數(shù)),但是此查詢(xún)卻只需要1秒(暫不考慮抽取到臨時(shí)表的耗時(shí)),這就是不同IO引起的差異。通常我們 使用的數(shù)據(jù)庫(kù)都是行式存儲(chǔ)的,可以簡(jiǎn)單的理解為,一行數(shù)據(jù)從頭讀到尾,才能進(jìn)入到下一行。這樣,不管一行中的200列,你只讀取其中的一列還是幾列,其余 的190多列仍然需要一定的IO。在大數(shù)據(jù)量下,這個(gè)性能差異就很明顯了。所以上面的這個(gè)例子就是一種典型的優(yōu)化手段,索引覆蓋也是處理類(lèi)似問(wèn)題的典型方 法,各位自行了解吧。列式存儲(chǔ)數(shù)據(jù)庫(kù)(如Sybase IQ)之所以性能這么高,也是同樣的道理。
-
盡量深入了解這些概念,如執(zhí)行計(jì)劃,基于開(kāi)銷(xiāo)的估算,統(tǒng)計(jì)信息等等。我用一句話(huà)來(lái)簡(jiǎn)單描述:數(shù)據(jù)庫(kù)通過(guò)統(tǒng)計(jì)信息來(lái)估計(jì)查詢(xún)開(kāi)銷(xiāo),統(tǒng)計(jì)信息不準(zhǔn)時(shí),開(kāi)銷(xiāo)估計(jì)就可能不準(zhǔn)確,從而導(dǎo)致選擇了錯(cuò)誤的執(zhí)行計(jì)劃。
-
測(cè)試過(guò)程中數(shù)據(jù)的清理。性能測(cè)試過(guò)程中可能又會(huì)生成大量的數(shù)據(jù),積累到一定程度又會(huì)對(duì)性能結(jié)果造成影響,所以每一輪測(cè)試時(shí)都應(yīng)該清理掉之前測(cè)試過(guò)程中產(chǎn)生的數(shù)據(jù),保證每次測(cè)試是在相同的條件下進(jìn)行的。
-
性能測(cè)試過(guò)程中,如果定位到了某一個(gè)查詢(xún)或SQL有問(wèn)題,首先要確認(rèn)的是數(shù)據(jù)是否合理。通過(guò)查詢(xún)計(jì)劃來(lái)判斷是否按預(yù)期進(jìn)行了查詢(xún),如果不是,查看數(shù)據(jù)的分布是否真實(shí)。一般數(shù)據(jù)庫(kù)會(huì)提供很多種手段來(lái)進(jìn)行驗(yàn)證。