Laravel 11大批量填充需禁用事件与查询日志、改用DB::table()->insert()批量插入、分批500–1000条并手动释放内存、清空表优先用truncate()、唯一字段用fake()->unique()。

在 Laravel 11 中执行大批量数据填充(如 10 万+ 条记录)时,直接用 Model::factory()->count(100000)->create() 或循环调用 save() 极易触发内存溢出——根本原因不是数据量本身,而是 Eloquent 模型实例持续累积、N+1 关联插入、模型事件与查询日志未关闭等叠加效应。
禁用模型事件与查询日志
每个模型创建都会触发 creating、created 等事件,并可能激活 Observer;开发环境下默认开启的查询日志还会缓存全部 SQL。这两项是内存飙升的“隐形推手”。
- 用
Model::withoutEvents(fn() => ...)包裹整段填充逻辑,彻底跳过事件分发 - 显式调用
DB::disableQueryLog()(即使生产环境通常已关闭,也建议加一层保险) - 若需保留部分事件,可改用
Model::unsetEventDispatcher(),之后再手动恢复
改用原生批量插入替代 Eloquent 创建
Eloquent 的 create() 或 insert() 方法虽方便,但每条记录仍会走模型生命周期。超千条数据时,应完全绕过模型层,直写数据库。
- 构造二维关联数组:
['name' => 'xxx', 'email' => 'yyy', 'created_at' => now()] - 调用
DB::table('users')->insert($data),一次插入最多 1000 行(MySQL 默认 max_allowed_packet 限制) - 对含外键或关联字段的数据,先批量插主表,再用
DB::table()->whereIn('term_id', [...])->insert(...)补子表
严格分批 + 手动释放资源
单次处理 500–1000 条为安全阈值;超出后 PHP 内存不会自动回收,需主动干预。
- 用
for ($i = 0; $i 控制循环,避免 <code>range()预分配大数组 - 每批结束后加
gc_collect_cycles()强制垃圾回收(Laravel 11 已默认启用 Zend GC,但仍建议显式调用) - 若使用工厂生成主模型(如 Term),获取 ID 后立即
unset($terms),防止对象链滞留
外键与清空策略前置处理
Seeder 运行失败常因外键约束或唯一索引冲突,且默认静默中断——表看起来“空”,实则卡在某一批。
- 开头加
DB::statement('SET FOREIGN_KEY_CHECKS=0'),结尾再设回=1 - 需要重跑时,用
DB::table('users')->truncate()(比delete()快 10 倍以上,不触发事件) - 对有唯一索引的字段(如 email),改用
fake()->unique()->email(),而非反复调用fake()->email()


















