原本打算用mysql內建的mysqlslap做壓力測試,可惜這工具不給力,可控制的地方不多,尤其不能夠產生隨機的測試語句。遂改用super smack。貌似它風評還不錯。
安裝
1.從網上下載tar.gz的安裝包。http://vegan.net/tony/supersmack/
我下載到的最新版本是1.3。
2.解壓後運行./configure; make; make install
./configure的時候有些參數值得注意:
a) --with-mysql (支援mysql)
b) --with-pgsql (支援postgresql)
c) --with-mysql-lib=/mysql/lib (設定mysql lib庫的路徑)
d) --with-mysql-include=/mysql/include (設定mysql include的路徑)
3.安裝過程中可能會碰見這個錯誤:
辦法是編譯之前修改檔案super-smack-1.3/src/query.cc
第193行:
< int len = 0, num_recs = 0;
修改成:
> long len = 0; int num_recs = 0;
第199,200行
< int str_len = (*i).first.length();
< if((unsigned)p + str_len + 3 *sizeof(int) < (unsigned)p_end )
修改成:
> long str_len = (*i).first.length();
> if((long)p + str_len + 3 *sizeof(int) < (long)p_end )
第219行
< len = (unsigned)p - (unsigned)buf;
修改成:
> len = (long)p - (long)buf;
(以上修改方法摘抄自網上的一篇blog,可惜link我已經找不到了,真不是我抄襲)
4.安裝完成後,可以用內建的設定檔測試一下:
super-smack -d mysql ./smacks/select-key.smack 2 5
2 表示啟動2個用戶端,5 則表示每個用戶端輪詢5次。
測試過程可能會碰到一些問題(比如我碰到過找不到/tmp/mysql.sock),這是因為select-key.smack中一些參數設定和實際不符。修改一下就好了。
結果分析
[root@vm189 mysqltest]# super-smack -d mysql ./select_key.smack 20 100
Query Barrel Report for client smacker1
connect: max=0ms min=0ms avg= 0ms from 20 clients
Query_type num_queries max_time min_time q_per_s
select_index 2000 4910 147 6.59
很容易理解。結果包括一共執行了多少次查詢(num_queries),最大的一次查詢時間(max_time)是4910ms,耗時最少的一次查詢是147ms,每秒完成查詢(min_time)6.59次
定義自己的測試組態檔案
要做自訂的MySQL測試,當然就必須編輯自己的設定檔(.smack)。Super Smack之所以很靈活,就在於可以在它的設定檔中自訂很多的東西。當然,隨著而來的就是設定檔的相對複雜。理解它的設定檔的結構,是學會用Super Smack的關鍵所在。
下文將以select-key.smack為例,講解.smack設定檔的結構。
網上也有一些相關的link:http://imysql.cn/docs/High_Performance_MySQL/0596003064/hpmysql-CHP-3-SECT-3.html
client "admin"
{
user "root";
host "localhost";
db "test";
pass "";
socket "/data0/mysql/mysql.sock"; // this only applies to MySQL and is
// ignored for PostgreSQL
}
這部分定義了Super Smack串連資料庫需要用到的一些資訊,比如使用者名稱,機器名,資料庫名,密碼等等。很容易理解,不解釋。
// ensure the table exists and meets the conditions
table "http_auth"
{
client "admin"; // connect with this client
// if the table is not found or does not pass the checks, create it
// with the following, dropping the old one if needed
create "create table http_auth
(username char(25) not null primary key,
pass char(25),
uid integer not null,
gid integer not null
)";
min_rows "90000"; // the table must have at least that many rows
data_file "words.dat"; // if the table is empty, load the data from
//this file
gen_data_file "gen-data -n 90000 -f %12-12s%n,%25-25s,%n,%d";
// if the file above does not exist, generate it with the above shell command
// you can replace this command with anything that prints comma-delimited
// data to stdout, just make sure you have the right number of columns
}
這裡定義了要進行測試的資料庫表的資訊。包括:
建立這個表所使用的sql語句。如果Super Smack探索資料庫中沒有這個表的話,會使用這條sql語句建立表。
注意,這裡設定了client "admin",表明是用前面定義的admin這個client來建立表。
min_rows這個參數是一個對錶的約束,要求表內的記錄必須達到的數量。例子中的值是90000,表示表中必須要有90000條資料。
“ data_file "words.dat"; ”是定義了一個資料檔案,這個檔案中的每一行都可以作為表中的一條記錄。如果表http_auth沒有達到90000條資料,那麼Super Smack會從words.dat中讀取資料然後插入到表http_auth中。
gen_data_file "gen-data -n 90000 -f %12-12s%n,%25-25s,%n,%d"; -- 自動產生測試資料,如果words.dat為空白的話,Super Smack使用這條語句產生測試資料然後寫入到words.dat中。
gen-data是安裝Super Smack後提供的一個產生隨機資料的小程式。下面解讀一下它的命令格式:
-n 90000表示產生90000條記錄;
-f後面跟的是記錄的格式;
%S表示產生字串,%12-12s表示產生的字串長度在12與12之間;
%D表示產生數字,%d產生隨機數;
%N %n表示可以從1開始遞增的數值,可用於表中的主鍵,比如第一條記錄的%n就會被1代替,以此類推。
上面的這句“gen-data -n 90000 -f %12-12s%n,%25-25s,%n,%d” 輸出的每行是大概這樣的:
josamibdnjdb3,eyhkbsombltouujdrbwcrrcgb,3,485560280
qwdqweqwdwev4,regergergftyyujeqwqwecdfr,4,239013239
rhbtjrbtywlf5,edeffkergpqqmcxvkrtmbhwer,5,233021393
預設gen-data的輸出都是列印了標準輸出上,可以用重新導向讓gen-data的輸出結果寫入到檔案中。
需要指出的是,這裡採用的例子select-key.smack是對一個表做查詢測試,所以要求table中必須首先有一定量的資料。如果想做插入的測試,完全可以將min_rows設定成0,這樣,Super Smack也就不需要調用gen_data_file產生資料。
//define a dictionary
dictionary "word"
{
type "rand"; // words are retrieved in random order
source_type "file"; // words come from a file
source "words.dat"; // file location
delim ","; // take the part of the line before ,
file_size_equiv "45000"; // if the file is greater than this
//divive the real file size by this value obtaining N and take every Nth
//line skipping others. This is needed to be able to target a wide key
// range without using up too much memory with test keys
}
//define a query
query "select_by_username"
{
query "select * from http_auth where username = '$word'";
// $word will be substitute with the read from the 'word' dictionary
type "select_index";
// query stats will be grouped by type
has_result_set "y";
// the query is expected to return a result set
parsed "y";
// the query string should be first processed by super-smack to do
// dictionary substitution
}
dictionary和query組合在一起,定義了需要的測試查詢語句。
一條普通的查詢語句,比如:select * from http_auth where username = 'test'; 它其實是由一個模板和一個或者幾個參數構成的。select * from http_auth where username = '$word'就是這條語句的模板,而$word = test則是設定參數。
query部分定義了查詢語句的模板,而dictionary則產生隨機的各種參數。
query的結構很簡單,最重要的就是“ query "select * from http_auth where username = '$word'"; ”,這定義了查詢的模板;
dictionary的結構則非常的靈活,它可以有多種配置:
source_type "file"; -- 表示這個dictionary是一個檔案的形式;
source "words.dat"; -- 表示檔案名稱為words.dat;
delim ","; -- 表示words.dat中第一個","之前的部分是這個dictionary的內容;
type:rand -- 表示隨機的從中抽取值;
把它們綜合起來理解。
之前看過,words.dat的資料是這樣的:
josamibdnjdb3,eyhkbsombltouujdrbwcrrcgb,3,485560280
qwdqweqwdwev4,regergergftyyujeqwqwecdfr,4,239013239
rhbtjrbtywlf5,edeffkergpqqmcxvkrtmbhwer,5,233021393
那麼,這裡定義的dictionary其實是讀取word.dat,並將每一行的第一個字串(比如josamibdnjdb3)作為字典內的元素,再和query組合起來,就變成了實際的查詢語句:
select * from http_auth where username = 'qwdqweqwdwev4'
select * from http_auth where username = 'rhbtjrbtywlf5'
select * from http_auth where username = 'josamibdnjdb3'
type除了可以定義為rand,還可以定義為
seq,表示Values are used sequentially;
unique,Generate unique values using the same method as gen-data;
source_type除了可以定義為 file,還可以定義為
list,表示a user-supplied list of words, comma-separated;
template,表示the format to use when type is unique. For example, "jzawodn_%07d" generates values composed of jzawodn_ and a seven-digit number
舉例來說,可以這樣定義:
dictionary "gender" {
type: rand
source_type "list";
source "male,female";
}
它表示這個gender的dictionary只有兩個值,要麼是male,要麼是female
dictionary "name"
{
type "unique";
source_type "template";
source "%15s";
}
表示name這個dictionary是一個長度為15的字串,Super Smack將用gen-data命令產生這個字串。
很多時候,如果你的查詢範本需要多個參數的話,那麼你可能需要準備多個dictionary。比如要產生如 select * from tablename where age < 20 AND id = 1234這樣的查詢,就需要兩個dictionary,一個是age,一個是id。
當然,還有一種討巧的方案,只需要產生一個dictionary,定義它的每一行是這樣的:
Age < *** AND id = ***
而對應的query模板就是
select * from tablename where dictionary;
// define database client type
client "smacker1"
{
user "test"; // connect as this user
pass ""; // use this password
host "localhost"; // connect to this host
db "test"; // switch to this database
socket "/data0/mysql/mysql.sock"; // this only applies to MySQL and is
// ignored for PostgreSQL
query_barrel "2 select_by_username"; // on each round,
// run select_by_username query 2 times
}
main
{
smacker1.init(); // initialize the client
smacker1.set_num_rounds($2); // second arg on the command line defines
// the number of rounds for each client
smacker1.create_threads($1);
// first argument on the command line defines how many client instances
// to fork. Anything after this will be done once for each client until
// you collect the threads
smacker1.connect();
// you must connect after you fork
smacker1.unload_query_barrel(); // for each client fire the query barrel
// it will now do the number of rounds specified by set_num_rounds()
// on each round, query_barrel of the client is executed
smacker1.collect_threads();
// the master thread waits for the children, each child reports the stats
// the stats are printed
smacker1.disconnect();
// the children now disconnect and exit
}
這裡又定義了一個client,它是執行查詢語句所用到的client(最早的client是建立表的client,這兩個client許可權應該不一樣。)
query_barrel "2 select_by_username"; -- 表示這個client在一次迴圈中執行select_by_username這個查詢兩次;
main中則定義了Super Smack具體的執行流程。很簡單,不解釋。