在數據庫的應用開發中,常常會遇到性能和代價的之間矛盾。以作者在開發股市行情查詢和交易系統中遇到的問題為例,要在實時記錄1000多只股票每分鐘更新一次的行情數據的同時,響應大量並發用戶的數據查詢請求。考慮到性價比和易維護性,系統又要求在基於PC服務器,WindowsNT平台的軟硬件環境下實現。開始,我們采用了MSSQL Server6.5作為數據庫系統,用VisualC++6.0開發了訪問數據庫的前端,應用ODBC 數據接口,在進行了大量的數據庫配置和程序優化後,發現仍不能滿足性能要求。後采用SQLServer的DB-Library接口,繞過了ODBC解釋層,可以每秒更新行情數據30次,同時支持20-30個左右的並發用戶進行行情查詢,基本滿足要求(單台PC服務器,單PII3 50CPU,內存128M,SCSI硬盤)。有沒有可能進一步提高系統的性能和負載能力呢?經過分析,數據庫服務器是系統的瓶頸。當然,可以采用UNIX服務器+大型數據庫的系統平台,但其開發、運行、維護的費用比微機+WindowsNT平台的費用高出數倍。我們在其它一些系統的開發中,也經常遇到這樣的矛盾。如何在微機平台上建立大容量、高效率、易維護、高性價比的數據庫系統呢?
考察國內基於微機平台的數據庫應用系統,典型的如網易的分布式郵件系統,采用了FreeBSD+MySQL的平台,其容量、負載能力和響應速度都很優秀。作者查閱了MySQL的相關文檔,發現MySQL是GNU軟件(即OpenSource自由軟件)中非常優秀的數據庫系統,它完全符合SQL92(Entrylevel)和ODBC(level0-2)規范,在符合POSIX規范的操作系統上實現了非常高效的關系型數據庫管理系統。根據MySQL提供的文檔,它的數據操作堪稱所有數據庫中最高效的,Benchmark如下表:
Reading2000000rowsbyindex
DatabaseSeconds
MySQL367
MySQL_odbc464
db2_odbc1206
informix_odbc121126
ms-sql_odbc1634
Oracle_odbc20800
solid_odbc877
Sybase_odbc17614
Inserting(350768)rows
DatabaseSeconds
MySQL381
MySQL_odbc619
db2_odbc3460
informix_odbc2692
ms-sql_odbc4012
Oracle_odbc11291
solid_odbc1801
Sybase_odbc4802
(runonthesameNT4.0Machine)
----從MySQL的Benchmark中可以看到,MySQL的性能非常出眾(當然,測試的MySQL系統可能作了優化,被測數據可能是針對MySQL選擇的),而且MySQL提供了對WindowsNT的支持。WindowsNT+MySQL能否成為構建高性能數據庫應用的理想選擇呢?作者用MySQL 的數據接口改寫了程序,經過一段時間的運行,證明MySQL確實是高效而穩定的數據庫,非常適合構建大容量、高效率、易維護、高性價比的數據庫應用系統。現將MySQL的安裝、運行、開發的心得與大家共享。
二、MySQL的安裝和運行
首先從http://www.mysql.com/(國內用戶可以從http://www.freecode.com.cn/m irror/mysql/)下載MySQL的執行代碼及源代碼。注意,WindowsNT用戶要選擇NT下的執行代碼,我下載的是MySQL-shareware-3.22.32-win.zip。解包後執行Setup,按屏幕提示即可完成安裝。
拷貝MySQL根目錄(c:mysql)下的my-example.cnf到c:my.cnf,按文件中的提示編輯my.cnf(如果MySQL的根目錄是c:mysql,可暫不改動my.cnf)。在NT的控制台窗口中,進入MySQL的執行目錄(c:MySQLin),執行
C:MySQLinmys
qld-shareware--standalone
則MySQL的數據庫引擎啟動。打開另一NT控制台窗口,執行
C:mysqlinmysqlMySQL
建立數據庫連接,出現“MySQL>”提示符後,執行
MySQL>DELETEFROMuserWHEREHost='localhost'ANDUser='';
MySQL>QUIT
刪除所有的非授權用戶。
然後執行
C:mysqlinMySQLadminreload
C:mysqlinMySQLadmin-urootpasswordyour_passWord
其中,your_passWord是你選擇的數據庫管理員的口令,必須妥善保管。
如果要每次都以管理員身份連接數據庫,則編輯c:my.cnf,在[clIEnt]段中加入
:
user=root
password=your_passWord
如果要停止MySQL的數據庫引擎,可以執行
C:mysqlinMySQLadmin-u=root-pshutdown
按提示輸入管理員口令後,MySQL的數據庫引擎停止。
三、MySQL客戶端應用的開發
MySQL提供了豐富的數據接口API,包括C、C++、Perl、PHP、Python、TCL等API和 JDBC,ODBC接口。出於性能考慮,我們采用了MySQL的CAPI進行開發。現以VisualC++ 環境為例,作一簡單介紹。
新建一Win32ConsoleApplication的Project,把“c:mysqlinclude”添加到編譯選項的包含路徑中(在ProjectOptions中加入/I"d:MySQLinclude")。新建一m ain.c文件,主體如下:
#include
#include
#include
#include
intmain(intargc,char*argv[])
{
charszTargetDSN[]="test";
charszSqlText[500]="";
charaszFlds[25][25];
MySQL*myData;
MySQL_RES*res;
MySQL_FIELD*fd;
MySQL_ROWrow;
inti,j,k;
BOOLbCreate=TRUE;
if((myData=mysql_init((MySQL*)0))
//初始化數據結構
&&MySQL_real_connect(myData,NULL,
//連接數據庫
"root","your_passWord",szTargetDSN,
MySQL_PORT,NULL,0))
{
if(bCreate)
{
sprintf(szSqlText,//構造SQL語句
"createtablemytable"
//新建一張表
"(timedatetime,s1char(6),"
"s2char(11),s3int,s4int)");
if(MySQL_query(myData,szSqlText))
//執行SQL語句
{//執行SQL語句出錯
ErrLog("Can'tcreatetable");
MySQL_close(myData);
returnFALSE;
> }
}
sprintf(szSqlText,
"insertintomytable"
//向表中插入數據
"values('2000-3-1021:01:30',"
//注意時間的格式
"'Test','MySQLTest',2000,3)");
if(MySQL_query(myData,szSqlText))
{//執行SQL語句出錯
ErrLog("Can'tinsertdatatotable");
MySQL_close(myData);
returnFALSE;
}
sprintf(szSqlText,"select*frommytable");
if(MySQL_query(myData,szSqlText))
//進行數據檢索
{
//執行SQL語句出錯
MySQL_close(myData);
returnFALSE;
}
else
{
res=MySQL_store_result(myData);
//取得查詢結果
i=(int)MySQL_num_rows(res);
//取得有效記錄數
printf("Query:%s %ldrecordsfound:
",szSqlText,i);
for(i=0;fd=MySQL_fetch_fIEld(res);
i++)
strcpy(aszFlds[i],fd->name);
//取得各字段名
for(i=1;row=MySQL_fetch_row(res);)
//依次讀取各條記錄
{j=MySQL_num_fIElds(res);
//取得記錄中的字段數
printf("Record#%ld:- ",i++);
for(k=0;k
//輸出各字段的值
printf("Fld#%d(%s):%s ",k+1,aszFlds[k],
(((row[k]==NULL)||
(!strlen(row[k])))?"NULL":row[k]));
puts("============================== ");
}
MySQL_free_result(res);
}
}
else
{//連接數據庫出錯
ErrLog("Can'tconnecttotheMySQLserver");
MySQL_close(myData);
returnFALSE;
}
MySQL_close(myData);
returnTRUE;
}
對其中幾個函數作簡單說明,詳細說明,可參考MySQL文檔:
1.MYSQL*mysql_init(MYSQL*MySQL)
初始化一個類型為MYSQL的數據結構,為執行mysql_real_connect()做准備。參數 mysql為指向該結構的指針,如果mysql為NULL,則新建並初始化一個MYSQL的數據結構。新建的結構將在MySQL_close()中釋放。
若成功,返回初始化的MySQL數據結構的指針,否則返回NULL。
2.MYSQL*mysql_real_connect(MYSQL*MySQL,constchar*host,
constchar*user,cons
tchar*passwd,constchar*db,
unsignedintport,constchar*unix_socket,unsignedintclIEnt_flag)
與MySQL數據庫引擎建立連接。在執行進一步的數據操作之前,必須保證MySQL_re al_connect()成功返回。
參數mysql是MySQL_init()的返回值;
參數host是運行MySQL數據庫引擎的機器的TCP/IP主機名,如為NULL則默認為“lo calhost”;
參數user和passwd是MySQL數據庫的合法用戶和口令;
參數db是連接的數據庫名;
參數port,unix_socket和clIEnt_flag一般取默認值。
3.intmysql_query(MYSQL*MySQL,constchar*query)
執行query字符串中的SQL語句,query必須以0結尾。如果成功,返回0。
4.MYSQL_RES*mysql_store_result(MYSQL*MySQL)
返回SELECT,SHOW,DESCRIBE,EXPLAIN等語句執行的結果。函數新建一個MYSQL_ RES的數據結構,把結果存儲在該結構中。如果查詢沒有匹配的結果,則返回空數據集。處理完結果集後,必須調用MySQL_free_result()。
如果出錯,返回NULL,否則返回MySQL_RES結構的指針。
5.MYSQL_ROWmysql_fetch_row(MySQL_RES*result)
取回結果集中的下一條記錄,如果沒有記錄或出錯,返回NULL。一條記錄中的字段數可以用mysql_num_fIElds(result)獲得,各字段的值可以用row[0]到row[MySQL_nu m_fIElds(result)-1]的數組來訪問。
在工程的鏈接選項中,加入c:mysqlliblibmysql.lib的接口庫,把libMySQL.d ll復制到操作系統的system目錄下(c:winntsystem32),就可以編譯運行了。
到這裡,一個簡單的數據庫應用就開發完成了。當然,MySQL有一套功能豐富的AP I,大家可以查看文檔。另外,如果需要跨平台的移植性,可以考慮用MySQL的ODBC接口。可以自己配置MySQL的ODBC連接屬性,也可以下載myodbc-2.50.29-nt.zip工具包來配置。
四、總結
作者用MySQL在WindowsNT上構建了高性能、高穩定性的數據庫系統。這樣,既可以利用WindowsNT和VisualC++的友好界面,又可以獲得MySQL的強大功能。實踐證明,這種方案成本低(別忘了MySQL是免費的)、效率高、開發周期短、運行穩定(MySQL的穩定性在Yahoo、163、263等站點獲得了極高的評價)。
目前,我的股市行情查詢和交易系統已經獲得了每秒鐘記錄70-100條股票行情數據,同時響應50個並發用戶進行數據查詢的性能,而系統的開發、運行成本卻大幅降低。如果您正為PC上的數據庫效率發愁,不妨也來試試MySQL吧。