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

JDBC 批次插入百萬級數據:從 47 秒優化到 1 秒的實戰秘籍

     編輯:Java 綜合教學

痛點:大批量寫入 MySQL 為什麼這麼慢?

在業務系統中,數據導入、日誌落地、報表生成等場景常面臨「單次寫入 10 萬甚至百萬條記錄」的需求。若逐條執行 INSERT,網絡往返(RTT)與數據庫解析開銷會拖垮性能。JDBC 提供的 addBatch() / executeBatch() 是標準解法,但預設配置下效率往往不盡如人意。

基礎版:使用 PreparedStatement 批處理

核心步驟:關閉自動提交 → 累積批次 → 一次性執行 → 手動提交。

public class MysqlBatchUtil {
    private static final String SQL = "INSERT INTO db_test (param1,param2,param3,param4,param5) VALUES (?,?,?,?,?)";
    private static final String URL = "jdbc:mysql://localhost:3306/test";
    private static final String USER = "root";
    private static final String PASS = "123456";

    public void doStore() throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        Connection conn = DriverManager.getConnection(URL, USER, PASS);
        conn.setAutoCommit(false); // 關鍵:關閉自動提交

        PreparedStatement psts = conn.prepareStatement(SQL);
        long start = System.currentTimeMillis();

        for (int i = 0; i <= 100_000; i++) {
            psts.setString(1, i + "param1");
            psts.setString(2, i + "param2");
            psts.setString(3, i + "param3");
            psts.setString(4, i + "param4");
            psts.setString(5, i + "param5");
            psts.addBatch();
        }

        psts.executeBatch(); // 一次性發送
        conn.commit();       // 手動提交事務
        conn.close();

        System.out.println("耗時: " + (System.currentTimeMillis() - start) + " ms");
    }
}

測試結果:100,001 條,**4725 ms**(約 4.7 秒)。雖比單條插入快數十倍,但離「高性能」尚有差距。

進階版:啟用 MySQL 專屬重寫優化

MySQL JDBC 驅動預設不會真正將批次合併為單條 SQL,而是仍逐條發送。需在連接字串開啟兩個關鍵參數:

  • rewriteBatchedStatements=true:核心開關,驅動會將多條 INSERT INTO t VALUES (?,?) 重寫為 INSERT INTO t VALUES (?,?),(?,?)... 形式,單次網絡交互完成所有寫入。
  • useServerPrepStmts=false:禁用服務端預編譯,改用客戶端拼裝 SQL,配合重寫機制效果最佳。
// 只需修改連接字串
String optimizedUrl = URL + "?useServerPrepStmts=false&rewriteBatchedStatements=true";
Connection conn = DriverManager.getConnection(optimizedUrl, USER, PASS);
// 其餘代碼完全不變...

測試結果:同樣 100,001 條,**1213 ms**(約 1.2 秒)。性能提升近 4 倍!

深度解析:為什麼這兩個參數如此關鍵?

參數預設值作用機制性能影響
rewriteBatchedStatements false 驅動層將 Batch 合併為多值 INSERT 語句 網絡包數從 N 降為 1,消除 RTT 開銷
useServerPrepStmts true true 時使用服務端 PreparedStatement,無法重寫;false 時客戶端拼裝 必須配合 false 才能觸發重寫邏輯

生產環境最佳實踐清單

  1. 批次大小控制:單次 executeBatch() 建議 500~5000 條,避免 SQL 過大超過 max_allowed_packet 或導致 OOM。
  2. 分批提交:大數據量分多次 executeBatch() + commit(),平衡事務日誌與鎖持有時間。
  3. 參數類型優化:優先使用 setInt、setLong 等原生類型,減少字符串轉換開銷。
  4. 索引策略:導入期間可考慮暫時禁用非唯一索引,導入完成後重建,大幅降低 B+ 樹維護成本。
  5. 連接池配置:配合 HikariCP 等高性能連接池,設置合理的 maximumPoolSize 與 connectionTimeout。

延伸:百萬級數據的極致優化方向

  • LOAD DATA INFILE:MySQL 原生工具,繞過 SQL 層直接寫入存储引擎,速度可達批次 INSERT 的 10~20 倍,但需文件權限且不支持複雜邏輯。
  • 分區表/分庫分表:將寫入壓力分散到多個物理表/實例,配合多線程並發導入。
  • 異步寫入 + 消息隊列:業務線程發送消息到 Kafka/RocketMQ,消費端批量消費寫庫,實現削峰填谷與解耦。

總結

JDBC 批次插入的性能天花板,往往不在代碼邏輯,而在於驅動層面的網絡交互優化。只要加上 rewriteBatchedStatements=true&useServerPrepStmts=false,即可讓 MySQL 驅動釋放真實性能。配合合理的批次大小、事務控制與索引策略,百萬級數據導入輕松進入「秒級」俱樂部。

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