摘 要:本文首先提出了一個基于Mybatis框架下針對ORACLE的批量數(shù)據(jù)插入的性能問題,然后針對該問題進行了調(diào)查,給出了問題的解決方法,并針對提出的方法進行了性能測試和比較。本文給開發(fā)者解決基于java的批量數(shù)據(jù)插入的性能問題提供了一個思路。
關(guān)鍵詞:ORACLE;Mybatis;批量數(shù)據(jù)插入;性能優(yōu)化
中圖分類號:TP393.09
1 問題描述
在進行web項目的開發(fā)中,數(shù)據(jù)庫使用的是ORACLE11G,O/R層使用的是Mybaits3.1。在進行頁面的某個操作時,需要向DB中插入批量的數(shù)據(jù),數(shù)據(jù)大約不足1000條左右,開發(fā)者按照普通的寫法,通過for循環(huán),每次調(diào)用DB的insert操作進行一條數(shù)據(jù)的插入,從log文件分析,全部數(shù)據(jù)的插入大約需要4,5秒鐘,加上其他的處理,畫面刷新大約需要6秒鐘,無法滿足客戶的要求。
2 調(diào)查過程
針對該頁面反映慢的問題,分析得出批量數(shù)據(jù)插入的性能提升是問題的關(guān)鍵,因為它大約占用了整個處理的85%的時間,如果能將它的性能進行優(yōu)化,很大程度上能夠解決該問題。首先分析代碼發(fā)現(xiàn),該DB插入處理是通過for循環(huán)每次插入一條數(shù)據(jù)進行的。盡管大家知道Mybatis本身采用的是連接池技術(shù),每次DB操作不需要進行DB的打開和關(guān)閉,這樣節(jié)省了時間,但是進行上千次的sql文操作也需要花費大量的時間,因此考慮能不能執(zhí)行一次插入操作將所有數(shù)據(jù)登錄到DB中,即實現(xiàn)批量插入。通過調(diào)查,方案有以下2個。
方案一:針對mybatis框架的。對于不同的DB類型,有以下兩種寫法。
(1)DB類型為Mysql的情況,可以利用SQL:insert into table values ('a1','a2')('b1','b2')('c1','c2')...,Mybatis的map文件中的寫法如下。
insert into table (id,name) values
(2)如果DB使用的是ORACLE, 1)的寫法不適用,可以利用SQL:
insert into table select 'a1','a2' from dual union all select 'b1','b2' from dual union all select 'c1','c2' from dual union all...,
Mybatis的map文件中的寫法如下:
insert into table (id,name)
select #{item.id,jdbcType=VARCHAR},
#{item.name,jdbcType=VARCHAR}} from dual
但是,在一條命令SQL引擎默認是一次最多插入1000條記錄,最多2100個字段參數(shù),一次處理海量數(shù)據(jù)容易引起以下兩個錯誤:
The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Too many parameters were provided in this RPC request. The maximum is 2100.和The number of row value expressions in the INSERT statement exceeds the maximum allowed number of 1000 row values.
所以插入的時候還要處理一下,如果超過這2個指標的話,就需要做多次插入。以上(方案一)的基本原理就是將所有被插入的數(shù)據(jù)生成一個select子句,這樣通過執(zhí)行一次insert語句將該select的數(shù)據(jù)集插入到DB中。
方案二:不基于任何框架,利用PreparedStatement對象。
具體寫法如下
try {
String url = \"jdbc:oracle:thin:@IP:1521:orcl\"; // orcl為數(shù)據(jù)庫的SID
String user = \"oracle\";
String password = \"oracle\";
StringBuffer sql = new StringBuffer();
sql.append(\"insert into table(id,name) values (?,?)\");
Class.forName(\"oracle.jdbc.driver.OracleDriver\");
Connection con = (Connection) DriverManager.getConnection(url,user,password);
// 關(guān)閉事務(wù)自動提交
con.setAutoCommit(1);
Long startTime = System.currentTimeMillis();
PreparedStatement pst = (PreparedStatement) con.prepareStatement(sql.toString());
for (int i = 0; i < list.size(); i++) {
Item item = (Item)list.get(i);
pst.setString(1, item .getId());
pst.setString(2, item .getName());
// 把一個SQL命令加入命令列表
pst.addBatch();
}
// 執(zhí)行批量更新
pst.executeBatch();
// 語句執(zhí)行完畢,提交本事務(wù)
con.commit();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally{
If(pst!=1){
Try{pst.close();}catch(SQLException e){}
}
If(con!=1){
Try{conclose();}catch(SQLException e){}
}
}
3 性能測定
針對原來的方法和以上2種方法,進行了性能試驗,結(jié)果如下表1所示。
4 結(jié)束語
本文通過具體實例,描述了基于java對ORACL進行批量數(shù)據(jù)插入操作的性能優(yōu)化的方法。并針對提出的優(yōu)化方法進行了實際的性能測試,給出了各種方法的測試結(jié)果。為開發(fā)者進行java的批量數(shù)據(jù)插入提供了一個好的方法。
參考文獻:
[1]邱小彬,周南,虞萍.基于JAVA的批量數(shù)據(jù)導(dǎo)入導(dǎo)出探討[J].農(nóng)業(yè)網(wǎng)絡(luò)信息,2008-10-26.
[2]徐雯,高建華.基于Spring MVC及MyBatis的Web應(yīng)用框架研究[J].微型電腦應(yīng)用,2012-07-20.
[3]尹幫治.基于VC#的Excel表格與SQL Server數(shù)據(jù)庫的批量數(shù)據(jù)導(dǎo)入導(dǎo)出技術(shù)研究[J].企業(yè)技術(shù)開發(fā),2008-08-01.
作者單位:沈陽工學(xué)院,遼寧撫順 113122;東軟集團股份有限公司,沈陽 110179