前言
針對數據庫的性能測試,loadrunner自己支持sql server和oracle數據庫,這兩種數據庫能夠用loadrunner直接錄製進行測試。而咱們項目中使用的是mysql數據庫,針對用loadrunner測試mysql數據庫的方法網上也有不少介紹文章,主要有兩種方案。一種是利用ODBC鏈接測試mysql,可是這種方法配置比較麻煩,若是要錄製的話須要安裝支持ODBC鏈接的查詢分析器,這工具很差找,能找到的也只能算湊合能用。若是你們有興趣試試這種方法,能夠上網搜搜,學習配置一下。另一種方法是利用mysql lib庫進行測試,這也是loadrunner推薦的測試方法,模擬mysql客戶端鏈接數據庫進行增刪改查的操做進行測試,這種方法配置簡單,不過不能經過錄制的方法獲得測試腳本,須要必定的編程基礎,手工編寫測試腳本,固然,能夠參考loadrunner的聯機幫助+google進行。下面主要介紹第二種方法,如何利用mysql lib庫鏈接mysql數據庫進行性能測試。php
配置loadrunner鏈接使用mysql
Loadrunner不能直接跟mysql進行通訊,在使用loadrunner以前須要將一些庫文件和dll文件添加到loadrunner的安裝目錄中。這些文件須要添加到全部須要執行測試腳本的系統中,包括須要使用loadrunner控制器,虛擬用戶生成器和loadrunner代理的測試負載機。mysql
在附件中的MySQL LoadRunner libraries.zip中包含兩個文件夾,bin和include。將這兩個文件夾覆蓋loadrunner安裝目錄中的bin和include兩個文件夾(將bin和include文件夾中的文件分別拷貝到loadrunner安裝目錄中對應的bin和include文件夾中也能夠)。Loadrunner安裝目錄一般是下面兩種格式:sql
C:Program Files (x86)HPLoadRunner(32位操做系統) 和 C:Program FilesHPLoadRunner(64位操做系統)數據庫

而後,在編寫測試腳本時,就須要引用這些庫文件了,具體操做以下:編程
- 在loadrunner腳本中,須要將下面這行代碼添加到vuser_init()這是初始化函數以前,這樣作是爲了讓loadrunner在後續的操做中都能引用這些庫文件。
1數組 |
#include "Ptt_MySQL.h";服務器 |
- 還須要用一個函數來加載dll文件到內存中,將下面這行代碼添加到須要進行鏈接數據庫操做以前,這樣才能讓loadrunner鏈接到mysql數據庫。
1併發 |
lr_load_dll ("libmysql.dll");oracle |
這行代碼最好添加在vuser_init()函數中。app
1 2 3 4 5 |
#define MYSQLSERVER "SERVER_NAME" #define MYSQLUSERNAME "USER_NAME" #define MYSQLPASSWORD "PASSWORD" #define MYSQLDB "SCHEMA_NAME" #define MYSQLPORT "PORT" |
用實際的mysql數據庫鏈接信息替換引號中的參數值。
- 如今,vuser_init()函數應該看起來像這個樣子。

到如今,已經作好了loadrunner鏈接數據庫的準備,能夠對mysql數據庫進行增、刪、改、查的操做了。
用loadrunner進行mysql數據庫的查詢操做
如今,咱們能夠在loadrunner腳本中插入任意的函數來進行鏈接並查詢mysql數據庫了。下面是一段示例,在Action()函數中如何進行鏈接mysql數據庫並進行查詢的操做。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
//Declare MySQL Elements char sqQuery[512]; // The MySQL query to be executed #定義一個字符數組存儲查詢的sql語句 MYSQL *Mconn; // The MySQL connection string #mysql鏈接字符串 int MyRC; // The MySQL return code #mysql的返回碼,能夠判斷sql語句是否執行成功。 Action() { //Create the connection string to connect to the server and relevant database #建立一個鏈接字符串鏈接服務器和數據庫 Mconn = lr_mysql_connect(MYSQLSERVER, MYSQLUSERNAME, MYSQLPASSWORD, MYSQLDB, atoi(MYSQLPORT)); //Build an SQL query and save the query as sqQuery #構建一個sql語句存儲在sqQuery字符數組中 sprintf(sqQuery, "select username, password, secret from loadrunner_db.parameters"); //Execute the SQL query (sqQuery) against the server and database defined as Mconn #執行sql語句 lr_mysql_query(Mconn, sqQuery); //Save the values returned in the first row of the table #查詢的結果會存在一個表中,這裏取出第一行的數據。 //N.B. The row/cell values appear to be the "wrong way round" #注意,這裏的行和列是顛倒的,至關於返回的數據表中一列數據表示數據庫中表的一行數據。 lr_save_string(row[0][0].cell, "sUsername"); lr_save_string(row[1][0].cell, "sPassword"); lr_save_string(row[2][0].cell, "sSecret"); //Output the returned values #打印出查詢出來的結果 lr_output_message(lr_eval_string("Username: {sUsername}")); lr_output_message(lr_eval_string("Password: {sPassword}")); lr_output_message(lr_eval_string("Secret: {sSecret}")); //Disconnect from MySQL #斷開數據庫鏈接 lr_mysql_disconnect(Mconn); return 0; } |
提示:能夠用lr_mysql_query函數執行任何sql語句,若是是執行的select操做,會有返回數據,返回的數據存儲在一個多維數組中,能夠用下面的語句將數據取出。
注意,這裏的行和列是顛倒的,x表示列(第一列是0),y表示行(第一行是0)。
用loadrunner進行mysql數據庫的插入操做
插入操做,只需將sql語句換成插入的sql語句便可,其餘操做根據實際狀況更改。
下面是一段示例代碼:
1 |
sprintf(sqQuery, "insert into loadrunner_db.results (id, username,password) values ('1','tom','123456')"; |
這段語句是插入一條數據,但實際狀況是,咱們在測試時可能須要插入不少不一樣的數據,若是id是主鍵,用這條數據就會報錯,由於主鍵不能重複。因此咱們須要對插入的數據進行參數化,代碼就須要改爲:
1 |
sprintf(sqQuery, " insert into loadrunner_db.results (username,password) values("%s","%s")",lr_eval_string("{user}"),lr_eval_string("{pwd}")); |
在數據庫表中,若是id是主鍵,咱們將id設置成自增加,讓其每添加一條數據,id自動加1。
Sprintf函數的做用就是將字符串格式化後存入指定的變量中,那麼咱們能夠將須要參數化的部分格式化處理,而後參數化輸入的變量。%s表示格式化成字符串,後面的lr_eval_string("xxx")是輸入的參數,每個參數對應一個%s,用逗號分隔參數,參數化輸入的參數xxx,即變成lr_eval_string("{user}"),咱們將須要輸入的參數所有在user.dat文件中定義便可。
還有其餘格式化表示爲:%d,double型;%f,float型;%i,int型等等。
Update和delete的操做跟insert相似,參數化方法同樣。
一個完成的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
//Declare MySQL Elements char sqQuery[512]; // The MySQL query to be executed MYSQL *Mconn; // The MySQL connection string //Declare variables which can be sent to MySQL in this sample script double dDuration; int iMIN, iMAX, i, iRand; Action() { lr_start_transaction("0001_Example"); lr_think_time(1); dDuration=lr_get_transaction_duration("0001_Example"); lr_end_transaction("0001_Example",LR_AUTO); lr_save_datetime("%H:%M:%S", TIME_NOW, "sTime"); iRand = fRandInt (1,1000000); //Create the connection string to connect to the server and relevant database Mconn = lr_mysql_connect(MYSQLSERVER, MYSQLUSERNAME, MYSQLPASSWORD, MYSQLDB, atoi(MYSQLPORT)); //Connect to the MySQL server defined in ....LR_IncludesMySQLSettings.h //Build an SQL query and save the query as sqQuery sprintf(sqQuery, "insert into loadrunner_db.results (time, duration, returned_data)" "values ("%s","%f","%i")", lr_eval_string("{sTime}"), dDuration, iRand); //Execute the SQL query (sqQuery) against the server and database defined as Mconn lr_mysql_query(Mconn, sqQuery); lr_output_message("This SQL statement executed - [%s]",sqQuery); //Disconnect from MySQL lr_mysql_disconnect(Mconn); return 0; } |
如何進行性能測試
以上咱們介紹了怎麼利用loadrunner鏈接mysql數據庫進行增刪改查的操做,那麼咱們要對數據庫進行性能測試該怎麼操做呢,有了上面的方法,進行測試就好辦多了。
首先,咱們先肯定測試目標。經過分別對數據庫進行增、改、查的操做,測試出最大的併發操做數和最佳併發操做數,以及最大的執行速率和平均執行速率。咱們先定義一下,這裏的併發操做,並不是是同一時刻去操做數據庫,而是創建多個鏈接進行數據庫操做,並不必定在同一個時間點操做數據庫,因此在腳本中就不設置集合點了。
結合上面的例子,咱們須要對腳本進行修改,設置事務以及判斷事務是否成功。由於咱們須要測試在進行增、改、查時的性能,因此咱們將執行sql語句操做設置在一個事務中。由於須要循環的操做,讓每一個鏈接處在活動的狀態,爲了避免讓頻繁的鏈接、斷開數據庫,咱們將鏈接數據庫和關閉數據庫的操做分別放在vuser_init()和vuser_end()函數中,把執行增、改、查的操做放在Action()中,對於鏈接數據庫的操做,咱們還須要判斷是否鏈接成功,若是鏈接失敗,作一個標記,在後續的操做中跳過失敗的鏈接,不然會出現腳本錯誤,影響測試結果。判斷鏈接數據庫是否成功的腳本,能夠直接將頭文件Ptt_MySql.h中的鏈接數據庫的操做函數拷貝到vuser_init()中進行修改,添加一個事務來統計鏈接成功的次數。
完整腳本示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
#include "Ptt_Mysql.h" #include "mysql.h" #include "mysql_com.h" #include "mysql_time.h" #include "mysql_version.h" #include "typelib.h" #include "my_list.h" #include "my_alloc.h" #define MYSQLSERVER "192.168.1.252" #define MYSQLUSERNAME "root" #define MYSQLPASSWORD "root" #define MYSQLDB "test" #define MYSQLPORT "3306" char chQuery[1024]; int statu=1; int status=0; MYSQL *Mconn; char sql_query[256], sql_sub_query[50]; int i=0, res=0, conn_fail=0, conn_iter=0; char response[50];// to go vuser_init() { lr_load_dll("libmysql.dll"); //Mconn = lr_mysql_connect(MYSQLSERVER, MYSQLUSERNAME, MYSQLPASSWORD, MYSQLDB, atoi(MYSQLPORT)); lr_start_transaction("mysql_conn"); //設置一個鏈接數據庫的事務 /*------------------------------------------------------------------------*/ /* Initialise MySQL */ if(!(Mconn = mysql_init(NULL))) { lr_message("Error -1: Cannot initialize MySQL - %s", mysql_error(Mconn)); //return -1; statu = 0; lr_end_transaction("mysql_conn", LR_FAIL); //初始化數據庫失敗斷定事務失敗 } /*------------------------------------------------------------------------*/ do { /* Connect to database */ if (!mysql_real_connect(Mconn, MYSQLSERVER, MYSQLUSERNAME, MYSQLPASSWORD, MYSQLDB, atoi(MYSQLPORT), NULL, 0)) { conn_fail = -2; conn_iter++; sleep(100); } else conn_fail = 0; } while(conn_fail < 0 && conn_iter < 10); if (conn_fail < 0) { lr_message("Error -2: %s", mysql_error(Mconn)); //mysql_close(Mconn); //return -2; statu = 0; lr_end_transaction("mysql_conn", LR_FAIL); //鏈接數據庫失敗斷定事務失敗 } else { //lr_message("MySql - Good Connection"); //mysql_close(Mconn); statu = 1; lr_end_transaction("mysql_conn", LR_PASS); //鏈接數據庫成功,事務經過。 } return 0; } Action() { if(statu){ //成功鏈接數據庫後才進行執行sql的操做 lr_start_transaction("mysql_insert"); sprintf(chQuery, "INSERT INTO lr_test (username,password) VALUE("%s","%s")",lr_eval_string("{user}"),lr_eval_string("{pwd}")); //sprintf(chQuery,"SELECT username,password FROM lr_test"); //sprintf(chQuery,"UPDATE lr_test SET password = "%s" WHERE username='tom'",lr_eval_string ("{pwd}")); status = lr_mysql_query(Mconn, chQuery); if(status!=0){ //判斷sql是否執行成功 lr_end_transaction("mysql_insert",LR_FAIL ); }else{ lr_end_transaction("mysql_insert", LR_PASS); } } return 0; } vuser_end() { lr_mysql_disconnect(Mconn); //關閉數據庫鏈接 return 0; } |
以上是插入數據的操做,根據以前的介紹, 測試更新和查詢,只需修改sql語句便可。剩下的就是將腳本加載到負載生成器中,進行各類場景的配置、測試便可。
各類測試場景運行完以後,就是分析圖表的過程了,由於咱們測試是針對執行sql語句設置的事務,即經過一個事務就成功的執行一句sql語句,那麼事務的響應時間也就是執行sql所花的時間,能夠取平均事務響應時間做爲平均執行一句sql所花時間的度量。每秒事務數是在單位時間(秒)內執行sql的條數,做爲執行sql語句速率的度量。
在運行測試場景時,儘可能將運行時間設置長一些,這樣採集到的數據越多,結果越準確。分別模擬不一樣的虛擬用戶數來測試,不一樣的虛擬用戶數在運行場景時,每秒事務數是不同的,當達到必定的虛擬用戶數後,每秒事務數會下降,或者出現錯誤,此時測試便可結束,取每秒事務數最大的一組數據做爲測試結果。
以上就是loadrunner利用mysql lib庫測試mysql數據庫的一個大體過程。
參考資料
http://www.bish.co.uk/forum/index.php?topic=50.0
http://www.bish.co.uk/forum/index.php?topic=77.0
聲明:本文爲原創,做者爲 歸根落葉,轉載時請保留本聲明及附帶文章連接:https://www.bstester.com/2013/12/mysql-database-performance-testing-using-loadrunner
----------------------------------------如下爲本人代碼備忘-------------------------------
init模塊
#include "Ptt_MySQL.h"
vuser_init()
{
lr_load_dll ("libmysql.dll");
#define MYSQLSERVER "localhost"
#define MYSQLUSERNAME "root"
#define MYSQLPASSWORD "root"
#define MYSQLDB "wkcrm"
#define MYSQLPORT "3306"
return 0;
}

action塊裏面的代碼
//Declare MySQL Elements
char sqQuery[512]; // The MySQL query to be executed #定義一個字符數組存儲查詢的sql語句
MYSQL *Mconn; // The MySQL connection string #mysql鏈接字符串
int MyRC; // The MySQL return code #mysql的返回碼,能夠判斷sql語句是否執行成功。
Action()
{
//Create the connection string to connect to the server and relevant database
// #建立一個鏈接字符串鏈接服務器和數據庫
Mconn = lr_mysql_connect(MYSQLSERVER, MYSQLUSERNAME, MYSQLPASSWORD, MYSQLDB, atoi(MYSQLPORT));
//Build an SQL query and save the query as sqQuery #構建一個sql語句存儲在sqQuery字符數組中
sprintf(sqQuery, "select leads_id from wkcrm_leads");
//Execute the SQL query (sqQuery) against the server and database defined as Mconn
// #執行sql語句
lr_mysql_query(Mconn, sqQuery);
//Save the values returned in the first row of the table
// #查詢的結果會存在一個表中,這裏取出第一行的數據。
//N.B. The row/cell values appear to be the "wrong way round" #
// 注意,這裏的行和列是顛倒的,至關於返回的數據表中一列數據表示數據庫中表的一行數據。
lr_save_string(row[0][0].cell, "leads_id");
// lr_save_string(row[1][0].cell, "sPassword");
// lr_save_string(row[2][0].cell, "sSecret");
//Output the returned values #打印出查詢出來的結果
lr_output_message(lr_eval_string("leads_id: {leads_id}"));
// lr_output_message(lr_eval_string("Password: {sPassword}"));
// lr_output_message(lr_eval_string("Secret: {sSecret}"));
lr_mysql_disconnect(Mconn);//斷開數據庫鏈接
return 0;
}

發現以上方法只能鏈接本地數據庫,遠程數據庫不知道什麼緣由,鏈接不了。