摘 要:本文首先提出了一個(gè)基于Mybatis框架下針對(duì)ORACLE的批量數(shù)據(jù)插入的性能問(wèn)題,然后針對(duì)該問(wèn)題進(jìn)行了調(diào)查,給出了問(wèn)題的解決方法,并針對(duì)提出的方法進(jìn)行了性能測(cè)試和比較。本文給開(kāi)發(fā)者解決基于java的批量數(shù)據(jù)插入的性能問(wèn)題提供了一個(gè)思路。
關(guān)鍵詞:ORACLE;Mybatis;批量數(shù)據(jù)插入;性能優(yōu)化
中圖分類(lèi)號(hào):TP393.09
1 問(wèn)題描述
在進(jìn)行web項(xiàng)目的開(kāi)發(fā)中,數(shù)據(jù)庫(kù)使用的是ORACLE11G,O/R層使用的是Mybaits3.1。在進(jìn)行頁(yè)面的某個(gè)操作時(shí),需要向DB中插入批量的數(shù)據(jù),數(shù)據(jù)大約不足1000條左右,開(kāi)發(fā)者按照普通的寫(xiě)法,通過(guò)for循環(huán),每次調(diào)用DB的insert操作進(jìn)行一條數(shù)據(jù)的插入,從log文件分析,全部數(shù)據(jù)的插入大約需要4,5秒鐘,加上其他的處理,畫(huà)面刷新大約需要6秒鐘,無(wú)法滿足客戶的要求。
2 調(diào)查過(guò)程
針對(duì)該頁(yè)面反映慢的問(wèn)題,分析得出批量數(shù)據(jù)插入的性能提升是問(wèn)題的關(guān)鍵,因?yàn)樗蠹s占用了整個(gè)處理的85%的時(shí)間,如果能將它的性能進(jìn)行優(yōu)化,很大程度上能夠解決該問(wèn)題。首先分析代碼發(fā)現(xiàn),該DB插入處理是通過(guò)for循環(huán)每次插入一條數(shù)據(jù)進(jìn)行的。盡管大家知道Mybatis本身采用的是連接池技術(shù),每次DB操作不需要進(jìn)行DB的打開(kāi)和關(guān)閉,這樣節(jié)省了時(shí)間,但是進(jìn)行上千次的sql文操作也需要花費(fèi)大量的時(shí)間,因此考慮能不能執(zhí)行一次插入操作將所有數(shù)據(jù)登錄到DB中,即實(shí)現(xiàn)批量插入。通過(guò)調(diào)查,方案有以下2個(gè)。
方案一:針對(duì)mybatis框架的。對(duì)于不同的DB類(lèi)型,有以下兩種寫(xiě)法。
(1)DB類(lèi)型為Mysql的情況,可以利用SQL:insert into table values ('a1','a2')('b1','b2')('c1','c2')...,Mybatis的map文件中的寫(xiě)法如下。
insert into table (id,name) values
(2)如果DB使用的是ORACLE, 1)的寫(xiě)法不適用,可以利用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文件中的寫(xiě)法如下:
insert into table (id,name)
select #{item.id,jdbcType=VARCHAR},
#{item.name,jdbcType=VARCHAR}} from dual
但是,在一條命令SQL引擎默認(rèn)是一次最多插入1000條記錄,最多2100個(gè)字段參數(shù),一次處理海量數(shù)據(jù)容易引起以下兩個(gè)錯(cuò)誤:
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.
所以插入的時(shí)候還要處理一下,如果超過(guò)這2個(gè)指標(biāo)的話,就需要做多次插入。以上(方案一)的基本原理就是將所有被插入的數(shù)據(jù)生成一個(gè)select子句,這樣通過(guò)執(zhí)行一次insert語(yǔ)句將該select的數(shù)據(jù)集插入到DB中。
方案二:不基于任何框架,利用PreparedStatement對(duì)象。
具體寫(xiě)法如下
try {
String url = \"jdbc:oracle:thin:@IP:1521:orcl\"; // orcl為數(shù)據(jù)庫(kù)的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ù)自動(dòng)提交
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());
// 把一個(gè)SQL命令加入命令列表
pst.addBatch();
}
// 執(zhí)行批量更新
pst.executeBatch();
// 語(yǔ)句執(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 性能測(cè)定
針對(duì)原來(lái)的方法和以上2種方法,進(jìn)行了性能試驗(yàn),結(jié)果如下表1所示。
4 結(jié)束語(yǔ)
本文通過(guò)具體實(shí)例,描述了基于java對(duì)ORACL進(jìn)行批量數(shù)據(jù)插入操作的性能優(yōu)化的方法。并針對(duì)提出的優(yōu)化方法進(jìn)行了實(shí)際的性能測(cè)試,給出了各種方法的測(cè)試結(jié)果。為開(kāi)發(fā)者進(jìn)行java的批量數(shù)據(jù)插入提供了一個(gè)好的方法。
參考文獻(xiàn):
[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ù)庫(kù)的批量數(shù)據(jù)導(dǎo)入導(dǎo)出技術(shù)研究[J].企業(yè)技術(shù)開(kāi)發(fā),2008-08-01.
作者單位:沈陽(yáng)工學(xué)院,遼寧撫順 113122;東軟集團(tuán)股份有限公司,沈陽(yáng) 110179