性能測試新手常犯錯誤總結(二):為什麼我模擬的百萬測試數據是無效的?
來自專欄 性能測試
測試環境的重要性無需多說,大家都知道測試環境要盡量的模擬生產環境,當然也包括數據。這樣測試的結果才會更加準確的反應真實的性能。就連開發過程,都已經開始在大數據量下加壓開發了。那麼,關於測試數據,你了解多少呢?
通常說的測試數據可以分為兩類:一是為了測試性能而準備的數據,這是用來模擬「壓力」的數據也就是常說的數據量、歷史數據等。一般都會根據需求或者經驗很容易估算出來,比如案件年增長量為5%,去年數據量為100W,測試需要保證3年後系統仍可正常運行,那麼就需要計算並模擬出3年後的總數據量,在這個基礎上進行測試。二是用來輔助測試使用的數據比如有一個對案件進行打分的功能,只有符合一定條件的案件才會出現在打分列表中。那麼我們要測這個打分的操作,首先就要保證有可用的案件,這就需要去生成測試數據,該數據可能一經使用就失效了(已經打過分就不能再打了)。這樣,每次測試這個功能,就需要準備這樣一批數據。這裡的測試數據,更多的是和測試流程有關,是為了能夠正常的進行測試,而不是涉及到性能的。
我們這裡要說的是第一類,對性能測試結果產生直接影響的數據。先看兩個小案例,涉及到了案件表(T_AJ)和法院編號列(N_FY)、立案日期列(D_LARQ)。案件表中模擬了一百萬測試數據,測試簡單的查詢操作,根據經驗,預期響應時間在2秒之內。案例1. 查詢本院案件列表,相應的SQL如下:select * from T_AJwhere N_FY=10order by D_LARQ desc執行這個操作耗時近10s,顯然達不到正常預期。經排查,生成的100W測試數據中,所有的N_FY列值都為10。這樣,最明顯的問題就是,查詢的結果集數量完全偏離了正常範圍。如果實際有100家法院,正常分布下,每家法院只有1W的案件,但測試數據的FY只有一個值,通過這個查詢,查出了原來100家法院的數據。無論是在資料庫處理中(如本例的排序),還是在程序的處理中(如展現或者是對數據做進一步處理),兩者的性能差異都是很顯著的。所以這個測試結果是無效的。有人說,這個例子太弱了,結果集差了100倍,性能當然不一樣了。那是不是數據總量和結果集大小都一致,測試結果就是有效了呢?案例2. 查詢本院一個月內收的案件,相應SQL如下:
select * from T_AJwhere N_FY=10 and D_LARQ between 20110101 and 20110201這個操作,查出來的結果只有一千條數據,屬於正常範圍。但查詢的時間還是超過5秒,依然超出了我們的預期。查看數據發現,N_FY=10的數據有近50萬,佔了總數據量的一半,D_LARQ在一月份的數據也佔了差不多一半。但是同時符合兩個條件的數據還是一千條左右。那麼這裡的問題就不在於結果集了,而是是否能利用索引進行查詢,看如下兩個圖就能很好理解了。在正常數據中,每家法院的數據可能佔總數據量的1%,一個月時間段內的數據可能佔總數據量更少,假設是0.5%。那麼這時我們通過N_FY和D_LARQ兩個條件進行查詢,資料庫會進行估算:符合D_LARQ查詢條件的數據大概有5000條,符合N_FY查詢條件的數據大概有1萬條,那麼用D_LARQ上的索引進行查詢是最快的,可以迅速的將查詢範圍縮小到5000條,然後在這5000條中去檢查N_FY是否也符合條件。過程如圖一所示(手繪草圖^_^)。圖一註:數據按行存儲,小方塊表示符合該列查詢條件的數據,陰影表示符合所有查詢條件,也就是最終的結果集。箭頭線段表示為了完成查詢,需要掃描的數據量,本圖中即符合LARQ查詢條件的數據。下同。
但在本例中不正常的數據條件下,資料庫會知道:符合N_FY查詢條件的數據有50萬條,符合D_LARQ的也有近50萬條,如果使用其中一列的索引將一百萬的範圍縮減到50萬,比從頭到尾掃描整個表做的工作還要多(為什麼呢?需要了解索引的結構和原理),那還是不要用索引了吧。於是資料庫會依次檢查每一條數據,判斷N_FY和D_LARQ是否符合條件。
如圖二所示。圖二註:本圖中實際掃描的數據量就是整張表的數據,但結果集和圖一是一樣大的。這樣,就可以知道,總數據量一樣,結果集大小一樣,為什麼性能差了很多了。就是因為數據分布不合理,導致資料庫無法正常使用索引,從而進行了全表掃描。當然,這個數據分布,我們依然可以歸類到結果集中去,那就是要保證每一個查詢條件「單獨的結果集」都要符合真實情況,而不僅僅是整個查詢最終的「總結果集」。看個這兩個簡單的小例子,我們再來總結一下關於測試數據,需要注意的內容:最根本、也是大家都知道的就是數據量,性能測試必須保證能在預期的數據量下進行測試。在一萬條記錄中查詢,和在一百萬數據中查詢,顯然是大大不同的,可以把數據量看做一種「壓力」,這個就不用再解釋了。但是在比較大型的系統中,這一點可能也不是很容易做好,因為這類系統往往有著複雜的資料庫,上百張的數據表。對每張表都進行數據模擬顯然是不現實的,也是沒有意義的,因為不是每張表都涉及到大數據量。那麼如何選取不容易遺漏呢?通常通過兩種方式:從設計和業務角度分析表間關係、從現有實際數據量進行分析推測。
1.確保結果集在正常範圍內。結果集的大小直接影響後續很多工作的性能,如數據排序分組、分頁、程序中的邏輯校驗或者是展現。2.數據分布必須合理,盡量接近真實。數據的分布,其實也就是數據的真實性,它直接決定了資料庫是否使用索引、選用哪個索引,也就是常說的查詢計劃。不同的查詢計劃也就是不同的數據訪問路徑,性能差別可能會很大。這裡主要涉及到的是索引的問題,需要大家對索引的原理有一定的了解,索引如何工作、資料庫如何選擇索引、和索引有關的一寫重要概念如區分度(selectivity)等等。1.最好的數據來自生產環境。這是顯而易見的,使用真實的數據測出來的結果才是最準確的。但是絕大多數情況下,我們沒有這樣的好運,可能是客戶禁止、也可能是生產環境數據量比較小。那就只好自己想辦法來模擬了,需要注意的也就是上面說到的幾點。這裡再推薦一種方法,數據翻倍。比如已經有了真實的數據十萬條,但我們需要一百萬條,那就可以通過寫一些SQL或者存儲過程,將現有的數據不斷翻倍(簡單的說,複製到臨時表,根據需要修改一些列,再插回到原表),這樣的數據真實性還是比較高的。關於測試數據,我想說的就是以上幾點了。另外再補充上一些相關內容,也是性能測試人員需要關注的。重點了解IO的概念,更準確的說應該是物理IO。一般來講,資料庫的瓶頸或者查詢的主要耗時就是IO。所以,資料庫優化的一個重要方向就是盡量減小IO。IO是不是只和數據量(行數)有關呢?舉一個例子:select co1, col2, col3, col4, col5 from T_AJwhere condition...
T_AJ數據量有100萬,表中有近200列,此查詢耗時大於10秒。而另一種實現方式,首先將col1-col5以及查詢條件中的幾個列的數據抽取到一張臨時表(#T_AJ)中。然後,select co1, col2, col3, col4, col5from #T_AJ where condition...臨時表#T_AJ和原數據表有同樣的數據量(行數),但是此查詢卻只需要1秒(暫不考慮抽取到臨時表的耗時),這就是不同IO引起的差異。通常我們使用的資料庫都是行式存儲的,可以簡單的理解為,一行數據從頭讀到尾,才能進入到下一行。這樣,不管一行中的200列,你只讀取其中的一列還是幾列,其餘的190多列仍然需要一定的IO。在大數據量下,這個性能差異就很明顯了。所以上面的這個例子就是一種典型的優化手段,索引覆蓋也是處理類似問題的典型方法,各位自行了解吧。列式存儲資料庫(如Sybase IQ)之所以性能這麼高,也是同樣的道理。盡量深入了解這些概念,如執行計劃,基於開銷的估算,統計信息等等。我用一句話來簡單描述:資料庫通過統計信息來估計查詢開銷,統計信息不準時,開銷估計就可能不準確,從而導致選擇了錯誤的執行計劃。測試過程中數據的清理。性能測試過程中可能又會生成大量的數據,積累到一定程度又會對性能結果造成影響,所以每一輪測試時都應該清理掉之前測試過程中產生的數據,保證每次測試是在相同的條件下進行的。性能測試過程中,如果定位到了某一個查詢或SQL有問題,首先要確認的是數據是否合理。通過查詢計劃來判斷是否按預期進行了查詢,如果不是,查看數據的分布是否真實。一般資料庫會提供很多種手段來進行驗證。推薦閱讀:
※Selenium Page Object 自動化測試框架-script測試腳本設計
※8年軟體測試工程師感悟——寫給還在迷茫中的朋友
※面向開發的測試技術(三):Web自動化測試
※如何從手工測試轉變為自動測試?
※Robotframwork安裝及簡介