addAll() 將多筆資料合併為單一 INSERT ... VALUES (...), (...), ... 語句,大幅優於循環 add()。但受限於 MySQL max_allowed_packet 參數,超大批次會導致「MySQL server has gone away」錯誤。
$User = M("User");
$dataList = [
['name' => 'thinkphp', 'email' => 'thinkphp@gmail.com'],
['name' => 'onethink', 'email' => 'onethink@gmail.com'],
];
$User->addAll($dataList);
// 生成 SQL:INSERT INTO `user` (`name`,`email`) VALUES ('thinkphp','...'),('onethink','...')
max_allowed_packet = 4MB(5.6+ 預設 4MB,8.0+ 預設 64MB)[mysqld]
max_allowed_packet = 100M
重啟 MySQL 生效:systemctl restart mysqld
function batchInsert($model, $dataList, $batchSize = 500) {
$chunks = array_chunk($dataList, $batchSize);
foreach ($chunks as $chunk) {
$model->addAll($chunk);
}
}
// 使用
batchInsert($User, $largeDataList, 200); // 每 200 筆一批
function safeBatchInsert($model, $dataList, $maxPacket = 16 * 1024 * 1024) {
// 估算單行平均長度
$sample = array_slice($dataList, 0, 10);
$avgLen = strlen($model->getLastSql()) / max(1, count($sample));
$safeBatch = max(1, floor($maxPacket * 0.8 / $avgLen)); // 留 20% 緩衝
foreach (array_chunk($dataList, $safeBatch) as $chunk) {
$model->addAll($chunk);
}
}
| 方式 | 1萬筆耗時 | SQL 數量 | 記憶體 |
|---|---|---|---|
| 循環 add() | ~15 秒 | 10,000 | 低 |
| addAll() 一次性 | ~0.8 秒 | 1 | 高(可能 OOM) |
| addAll() 分批(500) | ~1.2 秒 | 20 | 適中 |
$model->startTrans(); ... $model->commit();max_allowed_packet 與 innodb_log_file_sizeLOAD DATA INFILE 或 mysqldumpaddAll() 是批量寫入利器,但必須尊重 MySQL 協議限制。分批策略兼顧效能與穩定,是生產環境的標準做法。