首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

Java 多線程批次插入 1000 萬筆測試:1 分鐘完成,連接池與批次大小調優實錄

     編輯:MySQL 綜合教學

Java 多線程批次插入 1000 萬筆測試:1 分鐘完成,連接池與批次大小調優實錄

測試環境:4 核 CPU、MySQL 5.6、JDBC 直連(無連接池)、單表 test_teacher 8 個欄位。本文記錄從 10 線程×100 萬、20 線程×50 萬到 100 線程×10 萬的完整壓測數據,揭示「線程越多越快」的誤區,並給出生產級批次寫入最佳實踐。

一、核心代碼結構

public class MyThread extends Thread {
    @Override
    public void run() {
        Connection conn = DriverManager.getConnection(url, user, pass);
        conn.setAutoCommit(false);  // 關鍵:手動提交事務
        PreparedStatement ps = conn.prepareStatement("");

        String prefix = "INSERT INTO test_teacher (t_name,t_password,sex,description,pic_url,school_name,regist_date,remark) VALUES ";

        for (int batch = 1; batch <= 10; batch++) {      // 10 次提交
            StringBuilder suffix = new StringBuilder();
            for (int row = 1; row <= 100_000; row++) {   // 每批 10 萬行
                suffix.append("('").append(batch * row).append("','123456','男','教師','www.bbb.com','Java大學','2016-08-16 14:43:26','備注'),");
            }
            String sql = prefix + suffix.substring(0, suffix.length() - 1);
            ps.addBatch(sql);
            ps.executeBatch();   // 批次執行
            conn.commit();       // 提交事務
        }
        ps.close(); conn.close();
    }
}

二、壓測結果對比

線程數每線程筆數總筆數耗時吞吐 (萬/分)備註
10100 萬1000 萬~55 秒18.2最佳平衡點
2050 萬1000 萬~68 秒14.7線程切換開銷上升
10010 萬1000 萬OOM 崩潰-堆記憶體耗盡

三、關鍵發現與優化建議

  1. 批次大小 1000~5000 行最佳:單次 executeBatch 過大導致網路封包分片、GC 壓力;過小則 RPC 開銷佔比高。建議 1000~5000 行/批,配合 rewriteBatchedStatements=true(MySQL Connector/J 參數)自動重寫為多值 INSERT。
  2. 使用連接池(HikariCP)而非逐線程建連:測試代碼每線程獨立 DriverManager.getConnection,生產必須共用連接池,設定 maximumPoolSize = CPU核心數 * 2。
  3. 關閉自動提交 + 手動分批提交:單條自動提交效能極差,批次提交可減少 99%+ 圓趟延遲。
  4. 避免超大 SQL 字串拼接:改用 PreparedStatement 參數綁定 + addBatch(),防止 SQL 注入、減少解析開銷、支援更大批次。
  5. 線程數 ≠ 越多越好:超過 CPU 核心數 2~4 倍後,上下文切換、鎖競爭、連接數耗盡導致效能下降甚至 OOM。
  6. 生產建議參數:
    # JDBC URL
    jdbc:mysql://host:3306/db?rewriteBatchedStatements=true&useServerPrepStmts=true&cachePrepStmts=true&prepStmtCacheSize=250&prepStmtCacheSqlLimit=2048
    
    # HikariCP
    maximumPoolSize=8 (4核機器)
    batchSize=2000

四、改良版生產級代碼片段

try (Connection conn = dataSource.getConnection();
     PreparedStatement ps = conn.prepareStatement(
         "INSERT INTO test_teacher (t_name,t_password,sex,description,pic_url,school_name,regist_date,remark) VALUES (?,?,?,?,?,?,?,?)")) {
    conn.setAutoCommit(false);
    for (int i = 0; i < totalRows; i++) {
        ps.setString(1, "name_" + i);
        ps.setString(2, "123456");
        ps.setString(3, "男");
        ps.setString(4, "教師");
        ps.setString(5, "www.bbb.com");
        ps.setString(6, "Java大學");
        ps.setTimestamp(7, Timestamp.valueOf("2016-08-16 14:43:26"));
        ps.setString(8, "備注");
        ps.addBatch();
        if (i % 2000 == 0) {  // 每 2000 行提交一次
            ps.executeBatch();
            conn.commit();
        }
    }
    ps.executeBatch();  // 剩餘
    conn.commit();
}

結語:百萬級批次寫入的關鍵在於「適度並發 + 大批次 + 連接池 + 重寫批次語句」。盲目增加線程反而有害。上述參數經多個專案驗證,可在 4 核 8G 機器上穩定達到 50~80 萬行/分寫入吞吐。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved