insertAll()比saveAll()更适合纯插入,因其绕过模型、不触发验证/事件/自动时间戳,仅拼接批量SQL,执行更快、内存更低、无冗余开销。

直接用 Db::insertAll(),别碰 saveAll() 除非你真需要模型层逻辑(验证、事件、自动时间戳)——否则它只是徒增开销。
为什么 insertAll() 比 saveAll() 更适合纯插入
saveAll() 是模型方法,会初始化验证器、触发 creating 钩子、检查字段映射、处理 json 类型、填充 create_time 等。这些在大批量导入时全是冗余成本。
-
insertAll()绕过模型,只拼 SQL:INSERT INTO table (a,b) VALUES (1,2),(3,4),...,执行快、内存低、无副作用 -
saveAll()在数据可信(如后台离线导入)时加['validate' => false]可提速,但仍比insertAll()多走模型初始化流程 - 若表有
created_at字段且需自动写入,insertAll()不支持——得手动塞进每条数据里,比如'created_at' => date('Y-m-d H:i:s')
insertAll() 的参数和分批控制
它接受两个参数:insertAll($data, $batchSize = null)。第二个参数不是“是否分批”,而是“每批多少条”——框架会自动切片并循环执行。
- 不传
$batchSize:一次性全插,风险高(Packets larger than max_allowed_packet are not allowed) - 传
400:框架内部调用array_chunk($data, 400),每批生成一条 INSERT 语句 - 注意:即使设了
$batchSize,它也不会自动开事务——必须自己包一层Db::transaction() - 返回值是总影响行数(
int),不是 ID 列表;主键为自增时也无法获取插入后的 ID
事务 + 分批 + 内存管理三件套
批量插入不是调个函数就完事,漏掉任意一环都可能出半截数据、内存溢出或超时。
立即学习“PHP免费学习笔记(深入)”;
- 必须用
Db::transaction(function () use ($data) { ... })包裹,异常时自动回滚;混用saveAll()和insertAll()在同一事务里可能失效(底层连接对象不同) - 单批别超 500 条:MySQL 默认
max_allowed_packet=4M,400 条中等长度记录较安全;可查SHOW VARIABLES LIKE 'max_allowed_packet';调整 - CLI 脚本要加
set_time_limit(0)和ini_set('memory_limit', '-1');每批执行后建议gc_collect_cycles(),尤其处理 10 万+ 数据时 - 别在循环里反复
new模型或Db实例,复用同一个Db::name('user')对象即可
Excel 导入场景下容易被忽略的坑
从 Excel 解析完转成二维数组后,直接丢给 insertAll() 很可能失败,因为字段不匹配、空值类型错、时间格式乱。
- Excel 表头是中文(如“创建时间”),但数据库字段是
created_at——必须做字段映射,不能靠顺序硬塞 - Excel 单元格看似为空,实际是
''或null或0,往INT NOT NULL字段插会报错;统一转成null或补默认值 - 日期列经 PhpSpreadsheet 读出来可能是 Excel 序号(如
44562),得用\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject()转,再格式化为'Y-m-d H:i:s' - 用
setReadDataOnly(true)和getRowIterator($start, $limit)分块读,别调toArray()——10 万行 Excel 直接崩内存
真正卡住人的从来不是语法,而是事务没包住、分批数设太大、Excel 空值没清洗、或者以为 insertAll() 会自动填时间戳——这些点不逐个对齐,跑起来不是报错就是丢数据。



















