大量資料導入 (ETL、報表) 耗時常達數小時。本文實測三種優化手段對 `INSERT` 效能的影響:合併多值、事務包裹、主鍵有序寫入。
| 策略 | 原理 | 適用場景 | 注意事項 |
|---|---|---|---|
多值單語句INSERT ... VALUES (...), (...), ... | 減少 Binlog/Redo Log 刷盤頻率、SQL 解析次數、網路往返 | 中小批次導入 | 受 `max_allowed_packet` 限制 (預設 1MB,建議調大至 8MB+) |
事務包裹START TRANSACTION; ... COMMIT; | 將多次 Redo Log 合併一次提交,減少 `fsync` | 配合多值語句使用 | 事務過大超過 `innodb_log_buffer_size` 會觸發刷盤,建議分批提交 (如每 1000~5000 行) |
| 主鍵有序寫入 | 避免 B+Tree 頁分裂與合併,順寫極大提升索引維護效率 | 自增主鍵或有序業務鍵導入 | 無序導入隨機 IO 激增,千萬級數據性能崩塌 |
max_allowed_packet = 64M innodb_log_buffer_size = 64M innodb_flush_log_at_trx_commit = 2 # 導入期間可暫設 2,事後改回 1 innodb_buffer_pool_size = 物理記憶體 70%~80%
$batch = []; $batchSize = 1000; $commitSize = 5000;
$pdo->beginTransaction();
foreach ($rows as $i => $row) {
$batch[] = "('{$row['a']}', '{$row['b']}')";
if (count($batch) >= $batchSize) {
$sql = "INSERT INTO tbl (a,b) VALUES " . implode(',', $batch);
$pdo->exec($sql);
$batch = [];
}
if ($i % $commitSize === 0) { $pdo->commit(); $pdo->beginTransaction(); }
}
if ($batch) { $pdo->exec("INSERT INTO tbl (a,b) VALUES " . implode(',', $batch)); }
$pdo->commit();