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

1、解析問題。

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

2、解決問題。

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

關鍵代碼:git

//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;

}複製代碼

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

//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批和事務的使用。github

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

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

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

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

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

本節Demo源碼附帶測試數據包及數據庫腳本GitHub下載地址:框架

github.com/Breaker-93/…工具

如有任何疑問,請留言。

相關文章
相關標籤/搜索