后端開發(fā):【批量插入海量數(shù)據(jù)之Java插入MySql】解決方案

一朝氓、解析問題。

Java向MySql數(shù)據(jù)庫插入萬級記錄時主届,采用的方案不同時執(zhí)行速度會有所不同赵哲,數(shù)據(jù)量越大則優(yōu)劣越明顯。所以選取最優(yōu)方案尤其重要君丁,本文目前提供如下兩種解決方案(不借用第三方框架或工具)枫夺。


二、解決問題绘闷。

1橡庞、方案一:循環(huán)逐條插入。

關(guān)鍵代碼:

//DataModel 為自定義的數(shù)據(jù)模型類印蔗,dataList 即傳入的即將要插入的數(shù)據(jù)集合扒最;

public int insertData(List<DataModel> dataList) throws ClassNotFoundException, SQLException{

//開始計時;

Long begin = new Date().getTime();

//創(chuàng)建要執(zhí)行的sql語句华嘹;

String sql = "insert into tb_ncdc values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";

/* 創(chuàng)建并獲取JDBC連接類"Connection"的實例對象吧趣。(DBUtil類內(nèi)為數(shù)據(jù)庫訪問的配置信息,需要自定義)*/

Connection connection = new DBUtil().getDbCon();

//PrepareStatement類存放每條記錄對應(yīng)的字段值耙厚;

PreparedStatement preparedStatement= connection.prepareStatement(sql);

for (int i = 0; i < dataList.size(); i ++) {

preparedStatement.clearParameters();

preparedStatement.setString(1, dataList.get(i).getSTN());

preparedStatement.setString(2, dataList.get(i).getWBAN());

preparedStatement.setString(3, dataList.get(i).getYEARMODA());

preparedStatement.setString(4, dataList.get(i).getTEMP());

preparedStatement.setString(5, dataList.get(i).getDEWP());

preparedStatement.setString(6, dataList.get(i).getSLP());

preparedStatement.setString(7, dataList.get(i).getSTP());

preparedStatement.setString(8, dataList.get(i).getVISIB());

preparedStatement.setString(9, dataList.get(i).getWDSP());

preparedStatement.setString(10, dataList.get(i).getMXSPD());

preparedStatement.setString(11, dataList.get(i).getGUST());

preparedStatement.setString(12, dataList.get(i).getMAX());

preparedStatement.setString(13, dataList.get(i).getMIN());

preparedStatement.setString(14, dataList.get(i).getPRCP());

preparedStatement.setString(15, dataList.get(i).getSNDP());

preparedStatement.setString(16, dataList.get(i).getFRSHTT());

preparedStatement.execute();

?}

/*如果autocommit=false時(默認(rèn)為true强挫,即自動提交事務(wù))記得將本次事務(wù)提交,否則數(shù)據(jù)庫里沒有數(shù)據(jù)的薛躬;*/

//connection.commit();

//所有數(shù)據(jù)庫操作結(jié)束后記得關(guān)閉連接俯渤,減少內(nèi)存的占用;

preparedStatement.close();

connection.close();

// 結(jié)束時間

Long end = new Date().getTime();

//總 耗時

System.out.println("插入"+dataList.size()+"條數(shù)據(jù)的總時間為 : " + (end - begin)? + " ms");

return 1;

}

2型宝、方案二:分批事務(wù)插入八匠。

//DataModel 為自定義的數(shù)據(jù)模型類,dataList 即傳入的即將要插入的數(shù)據(jù)集合趴酣;

public int insertData(List<DataModel> dataList) throws ClassNotFoundException, SQLException {

//設(shè)定每批梨树、每次事務(wù)插入多少條數(shù)據(jù);

int itemNum = 1000;

//開始時間价卤;

Long begin = new Date().getTime();

// 創(chuàng)建sql前綴

String prefix = "INSERT INTO tb_ncdc VALUES ";

/* 創(chuàng)建并獲取JDBC連接類"Connection"的實例對象。(DBUtil類內(nèi)為數(shù)據(jù)庫訪問的配置信息渊涝,需要自定義) */

Connection connection = new DBUtil().getDbCon();

// PrepareStatement類存放每條記錄對應(yīng)的字段值慎璧;

PreparedStatement preparedStatement= connection.prepareStatement("");

// 創(chuàng)建sql后綴

StringBuffer suffix = new StringBuffer();

// 設(shè)置事務(wù)為非自動提交

connection.setAutoCommit(false);

//根據(jù)總的數(shù)據(jù)量計算需要分多少次事務(wù)插入床嫌;

int numTrans = dataList.size() / itemNum + 1;

//設(shè)定首次事務(wù)中的數(shù)據(jù)在集合中的索引為0;

int numData = 0;

// ?外層循環(huán)胸私,j代表提交事務(wù)次序厌处;

for (int j = 1; j <= numTrans; j++) {

// 從索引numData開始查找總數(shù)為itemNum個數(shù)據(jù),即為本批要插入的數(shù)據(jù)量岁疼;

for (int i = numData; i < numData + itemNum; i++) {

//判定如果是最后一批阔涉,可能會不足itemNum數(shù)量,則夠數(shù)結(jié)束捷绒,防止數(shù)組越界瑰排;

if (i == dataList.size()) {

break;

}

// 構(gòu)建sql后綴

suffix.append("('" + dataList.get(i).getSTN() + "','" + dataList.get(i).getWBAN() + "','"

+ dataList.get(i).getYEARMODA() + "','" + dataList.get(i).getTEMP() + "','"

+ dataList.get(i).getDEWP() + "','" + dataList.get(i).getSLP() + "','"

+ dataList.get(i).getSTP() + "','" + dataList.get(i).getVISIB() + "','"

+ dataList.get(i).getWDSP() + "','" + dataList.get(i).getMXSPD() + "','"

+ dataList.get(i).getGUST() + "','" + dataList.get(i).getMAX() + "','"

+ dataList.get(i).getMIN() + "','" + dataList.get(i).getPRCP() + "','"

+ dataList.get(i).getSNDP() + "','" + dataList.get(i).getFRSHTT() + "'),");

}

// 構(gòu)建完整sql

String sql = prefix + suffix.substring(0, suffix.length() - 1);

// 添加sql批;

preparedStatement.addBatch(sql);

// 執(zhí)行sql批暖侨;

preparedStatement.executeBatch();

// 提交本次事務(wù)

connection.commit();

// 清空上一次的sql后綴椭住;

suffix = new StringBuffer();

numData += itemNum;

}

// 所有數(shù)據(jù)庫操作結(jié)束后記得關(guān)閉連接,減少內(nèi)存的占用字逗;

preparedStatement.close();

connection.close();

// 結(jié)束時間

Long end = new Date().getTime();

// 耗時

System.out.println("插入" + dataList.size() + "條數(shù)據(jù)的總時間為 : "+ (end - begin) + " ms");

return 1;

}

三 京郑、總結(jié)問題。

1.兩種方案的主要區(qū)別在于葫掉,sql語句的不同些举、batch批和事務(wù)的使用。

單條插入sql語句:insert into Table (col1,col2...) values (val11,val12...);

多條批插入sql語句:insert intoTable (col1,col2...) values (val11,val12...),(val11,val12...),...;

2.本次測試的實例中俭厚,插入69萬條數(shù)據(jù)左右户魏,方案二要比方案一的速度快上10倍左右。具體測試得到的具體毫秒數(shù)可能不同套腹。影響因素個人認(rèn)為有如下幾條:

? ? (1)數(shù)據(jù)模型绪抛,每條數(shù)據(jù)記錄的字段越多,就需要調(diào)整itemNum(每批插入的數(shù)據(jù)量电禀,可以采用二分法找到最合適的數(shù)值)幢码,或者調(diào)整MySql數(shù)據(jù)庫對每次執(zhí)行sql語句的字節(jié)長度限制(網(wǎng)上自行搜索)。itemNum值找到最合適的尖飞,速度才可能在其他條件同等的條件下是最快的症副;

? ? (2)主機配置。包括處理器性能政基、硬盤性能 贞铣,mysql數(shù)據(jù)庫可能也會影響到速度;

【本節(jié)Demo源碼附帶測試數(shù)據(jù)包及數(shù)據(jù)庫腳本GitHub下載地址:

https://github.com/Breaker-93/Demo0929breaker_BatchInsertion.git

若有任何疑問沮明,請留言辕坝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市荐健,隨后出現(xiàn)的幾起案子酱畅,更是在濱河造成了極大的恐慌琳袄,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纺酸,死亡現(xiàn)場離奇詭異窖逗,居然都是意外死亡,警方通過查閱死者的電腦和手機餐蔬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門碎紊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人樊诺,你說我怎么就攤上這事仗考。” “怎么了啄骇?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵痴鳄,是天一觀的道長。 經(jīng)常有香客問我缸夹,道長痪寻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任虽惭,我火速辦了婚禮橡类,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘芽唇。我一直安慰自己顾画,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布匆笤。 她就那樣靜靜地躺著研侣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪炮捧。 梳的紋絲不亂的頭發(fā)上庶诡,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機與錄音咆课,去河邊找鬼末誓。 笑死,一個胖子當(dāng)著我的面吹牛书蚪,可吹牛的內(nèi)容都是我干的喇澡。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼殊校,長吁一口氣:“原來是場噩夢啊……” “哼晴玖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤呕屎,失蹤者是張志新(化名)和其女友劉穎宪萄,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體榨惰,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年静汤,在試婚紗的時候發(fā)現(xiàn)自己被綠了琅催。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡虫给,死狀恐怖藤抡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情抹估,我是刑警寧澤缠黍,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站药蜻,受9級特大地震影響瓷式,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜语泽,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一贸典、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧踱卵,春花似錦廊驼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至西饵,卻和暖如春酝掩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工暖夭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留软瞎,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓彻消,卻偏偏與公主長得像,于是被迫代替她去往敵國和親宙拉。 傳聞我的和親對象是個殘疾皇子宾尚,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法煌贴,繼承相關(guān)的語法御板,異常的語法,線程的語...
    子非魚_t_閱讀 31,581評論 18 399
  • 一. Java基礎(chǔ)部分.................................................
    wy_sure閱讀 3,790評論 0 11
  • 本人的環(huán)境為Myeclipse10牛郑、MySQL5.7.15 本文包括:簡介JDBC編程步驟打通數(shù)據(jù)庫程序詳解—Dr...
    廖少少閱讀 3,927評論 7 39
  • 本節(jié)介紹Statement接口及其子類PreparedStatement和CallableStatement怠肋。 它...
    zlb閱讀 1,134評論 0 0
  • 最近础芍,做了一件非常明智的事情杈抢,就是又開了一個公眾號JudyBBS-2,專門分享精選育兒資訊仑性,為各位寶媽服務(wù)惶楼。而將這...
    JudyBBS閱讀 1,091評論 0 0