每批独立事务控制可避免大事务风险,即每批数据用db::transaction()单独包裹,array_chunk分块插入,配合cli优化确保大批量导入稳定高效。

每批独立事务控制,就是让每一批数据的插入各自拥有完整的“开始-执行-提交/回滚”流程,互不影响。这样即使某一批失败,前面已成功批次的数据不会被回滚,后续批次仍可继续执行——适合大批量导入、容错要求高、或数据来源不可靠的场景。
为什么不能全量包在一个事务里
大事务会锁表时间长,阻塞其他写操作;容易触发 MySQL max_allowed_packet 限制或 PHP 超时;内存持续占用不释放,尤其 CLI 下易 OOM。单事务全量插入 10 万条,风险远大于收益。
每批用 Db::transaction() 单独包裹
这是最清晰、最可控的做法。对每个分块调用一次事务闭包,框架自动捕获异常并回滚该批次:
- 用 array_chunk($data, 400) 切成每批 400 条(兼顾 MySQL 包大小与执行稳定性)
- 循环中对每个 $chunk 调用 Db::transaction(fn() => Db::name('user')->insertAll($chunk))
- 每批成功后可记录日志或输出进度,如 echo "第 {$i} 批完成,插入 {$count} 条\n"
- 无需手动 try/catch,Db::transaction 已内置异常处理
注意模型层与 Db 层的隔离
如果用的是模型 saveAll(),同样可以每批独立事务,但必须确保:
- 关闭自动验证:UserModel::batchValidate(false)
- 禁用自动时间戳(若已赋值):protected $autoWriteTimestamp = false
- 显式启用批量模式:UserModel::saveAll($chunk, ['batch' => true])
- 事务必须用 Db::transaction() 包裹,不要在事务内混用 Db::insertAll() 和 UserModel::saveAll(),连接实例不同会导致事务失效
CLI 脚本需配套优化
避免因环境限制中断执行:
- 开头加 set_time_limit(0) 和 ini_set('memory_limit', '-1')
- 每批执行完调用 gc_collect_cycles(),防止内存缓慢增长
- 建议将逻辑封装为命令行指令,便于调度与重试
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











