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

JDBC 批處理實戰:Statement.addBatch() vs PreparedStatement.addBatch() 性能對比

     編輯:Java 綜合教學

JDBC 批處理核心技術:Statement 批量執行異構 SQL、PreparedStatement 批量參數化,性能提升 10 倍+

向數據庫發送大量 SQL 時,逐條 executeUpdate() 會產生嚴重網絡往返開銷。JDBC Batch 機制允許**客戶端攒批、一次性發送**,大幅降低網絡延遲。本文詳細演示 Statement 與 PreparedStatement 兩種批處理模式的代碼實現、適用場景、性能差異與生產級優化參數。

一、Statement 批處理:支援異構 SQL,無預編譯


public class StatementBatchDemo {
    public static void main(String[] args) {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            Connection conn = DriverManager.getConnection(
                "jdbc:mysql://localhost:3306/java", "root", "root");
            
            Statement stmt = conn.createStatement();
            // 關鍵:關閉自動提交,事務手動控制
            conn.setAutoCommit(false);
            
            // 可添加**完全不同結構**的 SQL
            stmt.addBatch("CREATE DATABASE IF NOT EXISTS batch_db");
            stmt.addBatch("USE batch_db");
            stmt.addBatch("CREATE TABLE t_batch (id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(20))");
            stmt.addBatch("INSERT INTO t_batch(name) VALUES('aaa')");
            stmt.addBatch("INSERT INTO t_batch(name) VALUES('bbb')");
            stmt.addBatch("INSERT INTO t_batch(name) VALUES('ccc')");
            
            // 一次性執行批次,返回每條 SQL 影響行數數組
            int[] results = stmt.executeBatch();
            
            conn.commit(); // 統一提交事務
            System.out.println("批次執行結果: " + Arrays.toString(results));
            
            stmt.close(); conn.close();
        } catch (Exception e) { e.printStackTrace(); }
    }
}

特點:

  • ✅ 支援 DDL + DML 混合、異構 SQL
  • ❌ **無預編譯**,每條 SQL 服務端都要解析優化,易受 SQL 注入
  • ❌ 參數無法參數化,需字符串拼接

二、PreparedStatement 批處理:參數化 + 預編譯 + 批量,性能王者


public class PreparedStatementBatchDemo {
    public static void main(String[] args) {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            Connection conn = DriverManager.getConnection(
                "jdbc:mysql://localhost:3306/java?rewriteBatchedStatements=true", "root", "root");
            // 關鍵連接參數:rewriteBatchedStatements=true (MySQL 驅動專用,將批次重寫為多值 INSERT)
            
            conn.setAutoCommit(false);
            
            String sql = "INSERT INTO t_batch(name, age, create_time) VALUES(?, ?, NOW())";
            PreparedStatement pstmt = conn.prepareStatement(sql);
            
            // 模擬 10 萬條數據批量入庫
            int batchSize = 1000; // 每 1000 條提交一次,防止內存/事務日誌過大
            for (int i = 1; i <= 100_000; i++) {
                pstmt.setString(1, "user_" + i);
                pstmt.setInt(2, 20 + i % 50);
                pstmt.addBatch();
                
                if (i % batchSize == 0) {
                    pstmt.executeBatch(); // 發送批次
                    conn.commit();        // 提交事務
                    pstmt.clearBatch();   // 清空批次緩衝
                }
            }
            // 剩餘不足 batchSize 的數據
            pstmt.executeBatch();
            conn.commit();
            
            pstmt.close(); conn.close();
            System.out.println("10萬條數據批量入庫完成");
        } catch (Exception e) { e.printStackTrace(); }
    }
}

三、核心性能差異對比

維度Statement.addBatchPreparedStatement.addBatch
SQL 解析每條獨立解析**預編譯一次**,多次復用執行計劃
參數傳遞字符串拼接 (注入風險)**參數化綁定**,類型安全
網絡往返批次發送一次批次發送一次
MySQL 優化無rewriteBatchedStatements=true 合併為 INSERT ... VALUES(...),(...),(...)
典型性能 (10萬條)~30-60 秒**~2-5 秒** (快 10-20 倍)
適用場景DDL 初始化、異構腳本、一次性遷移**業務數據大批量寫入、ETL、日志入庫**

四、生產級批處理優化清單

  1. 分批提交:每 500-2000 條 executeBatch() + commit() + clearBatch(),避免單事務過大導致鎖升級/日志膨脹/內存 OOM
  2. 關閉自動提交:conn.setAutoCommit(false),手動控制事務邊界
  3. MySQL 必加參數:JDBC URL 添加 rewriteBatchedStatements=true,驅動層將多條 INSERT 重寫為單條多值 INSERT,極大減少解析開銷
  4. 主鍵生成策略:批量插入時避免 AUTO_INCREMENT 鎖爭用,改用雪花算法/UUID/號段模式客戶端生成 ID
  5. 異常處理:executeBatch() 拋 BatchUpdateException,調用 getUpdateCounts() 獲取部分成功結果,決定重試/回滾策略
  6. 連接池配合:從池獲取連接 → 批處理 → 歸還池,避免頻繁建連

五、事務邊界與批次大小選型建議


// 經驗公式:批次大小 = min(可用內存 / 單條對象大小, 事務日誌容量 / 單條日誌大小, 2000)
// 典型值:
// - 寬表(50+列):500-1000
// - 窄表(10列):1000-2000
// - 極簡表(3列):2000-5000

壓測驗證:固定總數據量(如 100萬),測試不同 batchSize 耗時、內存、CPU,選擇拐點值。

六、總結

  • Statement 批處理:適用「一次性腳本執行、DDL+DML 混合、無參數化需求」
  • PreparedStatement 批處理 + rewriteBatchedStatements=true:**業務級大批量寫入首選**,性能提升數量級
  • 核心三板斧:關閉自動提交、分批提交、參數化預編譯

掌握 JDBC 批處理,是從「寫單條 SQL」進化為「工程化數據處理」的關鍵技能。下次遇到「導入 100 萬行 Excel 入庫」,你就知道該怎麼優雅地搞定了。

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