後端開發:「批量插入海量數據之Java插入MySql」解決方案

SQL MySQL Java 編程語言 Java團長 Java團長 2017-10-08

後端開發:「批量插入海量數據之Java插入MySql」解決方案

Java學習交流群:495273252

一、解析問題。

Java向MySql數據庫插入萬級記錄時,採用的方案不同時執行速度會有所不同,數據量越大則優劣越明顯。所以選取最優方案尤其重要,本文目前提供如下兩種解決方案(不借用第三方框架或工具)。

二、解決問題。

1、方案一:循環逐條插入。

關鍵代碼:

//DataModel 為自定義的數據模型類,dataList 即傳入的即將要插入的數據集合;public int insertData(List<DataModel> dataList) throws ClassNotFoundException, SQLException{//開始計時;Long begin = new Date().getTime();//創建要執行的sql語句;String sql = "insert into tb_ncdc values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";/* 創建並獲取JDBC連接類"Connection"的實例對象。(DBUtil類內為數據庫訪問的配置信息,需要自定義)*/Connection connection = new DBUtil().getDbCon();//PrepareStatement類存放每條記錄對應的字段值;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時(默認為true,即自動提交事務)記得將本次事務提交,否則數據庫裡沒有數據的;*/ //connection.commit(); //所有數據庫操作結束後記得關閉連接,減少內存的佔用; preparedStatement.close(); connection.close(); // 結束時間 Long end = new Date().getTime(); //總耗時 System.out.println("插入"+dataList.size()+"條數據的總時間為 : " + (end - begin) + " ms"); return 1;}

2、方案二:分批事務插入。

//DataModel 為自定義的數據模型類,dataList 即傳入的即將要插入的數據集合;public int insertData(List<DataModel> dataList) throws ClassNotFoundException, SQLException { //設定每批、每次事務插入多少條數據; int itemNum = 1000; //開始時間; Long begin = new Date().getTime(); // 創建sql前綴 String prefix = "INSERT INTO tb_ncdc VALUES "; /* 創建並獲取JDBC連接類"Connection"的實例對象。(DBUtil類內為數據庫訪問的配置信息,需要自定義) */ Connection connection = new DBUtil().getDbCon(); // PrepareStatement類存放每條記錄對應的字段值; PreparedStatement preparedStatement= connection.prepareStatement(""); // 創建sql後綴 StringBuffer suffix = new StringBuffer(); // 設置事務為非自動提交 connection.setAutoCommit(false); //根據總的數據量計算需要分多少次事務插入; int numTrans = dataList.size() / itemNum + 1; //設定首次事務中的數據在集合中的索引為0; int numData = 0; // 外層循環,j代表提交事務次序; for (int j = 1; j <= numTrans; j++) { // 從索引numData開始查找總數為itemNum個數據,即為本批要插入的數據量; for (int i = numData; i < numData + itemNum; i++) { //判定如果是最後一批,可能會不足itemNum數量,則夠數結束,防止數組越界; if (i == dataList.size()) { break; } // 構建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() + "'),"); } // 構建完整sql String sql = prefix + suffix.substring(0, suffix.length() - 1); // 添加sql批; preparedStatement.addBatch(sql); // 執行sql批; preparedStatement.executeBatch(); // 提交本次事務 connection.commit(); // 清空上一次的sql後綴; suffix = new StringBuffer(); numData += itemNum; } // 所有數據庫操作結束後記得關閉連接,減少內存的佔用; preparedStatement.close(); connection.close(); // 結束時間 Long end = new Date().getTime(); // 耗時 System.out.println("插入" + dataList.size() + "條數據的總時間為 : "+ (end - begin) + " ms"); return 1;}

三 、總結問題。

1.兩種方案的主要區別在於,sql語句的不同、batch批和事務的使用。

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

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

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

(1)數據模型,每條數據記錄的字段越多,就需要調整itemNum(每批插入的數據量,可以採用二分法找到最合適的數值),或者調整MySql數據庫對每次執行sql語句的字節長度限制(網上自行搜索)。itemNum值找到最合適的,速度才可能在其他條件同等的條件下是最快的;

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

後端開發:「批量插入海量數據之Java插入MySql」解決方案

原文作者

相關推薦

推薦中...