痛點:逐條 INSERT 極慢
// 反模式:循環逐條執行
for (int i = 0; i < list.size(); i++) {
String sql = "INSERT INTO rddform ... VALUES (...)";
stmt.executeUpdate(sql); // 每條一次網路往返 + 事務提交
}
// 本地 ~20 行/秒,雲端 ~3 行/秒,2000 條需 1.5~10 分鐘
優化:單條 SQL 批次插入 (Multi-row INSERT)
// 優化:拼接單條 SQL,一次提交
StringBuilder sb = new StringBuilder("INSERT INTO rddform (cols) VALUES ");
for (int i = 0; i < list.size(); i++) {
sb.append("(").append(vals).append(")");
if (i < list.size()-1) sb.append(",");
}
stmt.executeUpdate(sb.toString());
// 本地/雲端均 **秒級完成**,提速 100x+
核心優化原理
- 減少網路往返:1 次 RTT 替代 N 次
- 減少解析/優化開銷:1 次解析替代 N 次
- 減少事務提交/日誌刷盤:1 次提交替代 N 次 (若自動提交)
- InnoDB 批次寫入:減少 undo/redo log 同步開銷
生產環境關鍵配置與限制
| 參數/限制 | 預設值 | 建議調整 | 說明 |
| max_allowed_packet | 4MB/1MB | 16MB~64MB | 單條 SQL 最大長度,批次大小受限於此 |
| innodb_log_buffer_size | 8MB~64MB | 64MB~256MB | 大事務緩衝 redo log,避免頻繁刷盤 |
| bulk_insert_buffer_size | 8MB | 64MB~128MB | MyISAM 專用,InnoDB 無效 |
| SQL 長度限制 | max_allowed_packet | 分批提交 | 超長 SQL 需拆分多批次 |
最佳實踐清單
- 分批提交:每批 500~5000 行,視行大小調整,防超
max_allowed_packet。
- 手動事務控制:
SET autocommit=0; ... COMMIT; 而非自動提交。
- 參數化預處理:
PreparedStatement.addBatch() 更安全、可復用執行計劃。
- 關閉非必要索引/外鍵:大量導入時暫時
DROP INDEX、SET FOREIGN_KEY_CHECKS=0,結束後重建。
- 主鍵順序插入:按主鍵遞增順序插入,減少頁分裂與碎片。
- 監控
Innodb_log_waits:若增長快,說明 redo log 刷盤跟不上,擴大 innodb_log_file_size 或 innodb_log_buffer_size。
對比圖表說明
文中圖片展示:逐條插入 (藍線) 隨資料量線性增長耗時;批次插入 (紅線) 呈對數級增長,2000 行從 90 秒降至 <1 秒。
遷移到 Load Data (極致速度)
LOAD DATA INFILE '/path/data.csv'
INTO TABLE rddform
FIELDS TERMINATED BY ',' ENCLOSED BY '"'
LINES TERMINATED BY '\n'
(col1, col2, ...);
繞過 SQL 層,直接由存儲引擎寫入,**再快 5-10 倍**,適合 TB 級導入。
結論:**拒絕循環 INSERT,擁抱批次合併與 Load Data**,資料導入不再是瓶頸。
- 上一篇文章: MySQL 儲存過程循環構造全解析:WHILE、LOOP、REPEAT 與 LEAVE 跳出實戰
- 下一篇文章: MySQL UPDATE 實戰大全:單表、關聯、子查詢更新與避坑指南
Copyright ©
程式碼之家 All Rights Reserved