Laravel中如何低内存最快完成100万条记录的数据填充
Laravel 低内存高速填充100万条Seeder数据最优方案
核心问题先答
你提到的两种方案里,每次循环生成固定条数入库、再清空变量的方式更优。所谓chunk分块本身是针对「已存在的表数据做分批查询遍历」设计的API,不是给批量写入场景用的,硬套chunk写插入逻辑只会多一层无意义的回调封装开销,没有任何性能收益。
你贴的参考代码完全达不到预期效果,有两个致命问题:
- 调用
make()仅在内存生成模型对象,不会执行入库操作,跑完循环表里不会有任何数据 - 没有内存释放逻辑,循环生成100万个模型实例堆在内存中,运行过程必然触发内存溢出
性能拉满的实现方案
这套方案可以把内存稳定控制在10MB以内,普通开发机跑100万条填充耗时仅需十几到几十秒,比常规Eloquent create的写法快5-10倍:
第一步:提前关闭所有不必要的开销
在Seeder的run方法开头先执行以下配置,砍掉ORM和框架的冗余消耗:
public function run() { // 关闭数据库查询日志,这是批量插入场景下排名第一的内存杀手 DB::disableQueryLog(); // 临时注销模型事件监听,避免creating/created等钩子产生额外开销 User::flushEventListeners(); $totalCount = 1000000; $perBatch = 1000; // 单批插入条数,根据字段量可在500-2000区间调整
第二步:分批插入+主动释放内存
不要用Factory的create()方法(该方法会单条执行插入、每次实例化新模型,性能极差),生成模型数据后直接转数组,用底层查询构造器做批量插入:
for ($i = 0; $i < $totalCount / $perBatch; $i++) { // 通过工厂生成单批数据,直接转数组跳过模型实例化入库的开销 $userList = User::factory()->count($perBatch)->make()->toArray(); // 用原生查询构造器批量写入,比ORM create快数倍 DB::table('users')->insert($userList); // 主动释放变量引用,触发内存回收 unset($userList); gc_collect_cycles(); } }
额外提速技巧(可再快2-3倍)
如果追求极致速度,可以叠加以下优化:
- 写入前临时删除表上的非主键索引、外键约束,等所有数据插入完成后再重建,避免数据库每次写入都维护索引结构
- 针对MySQL数据库,可以把所有插入操作包在同一个事务中,减少单条SQL刷盘的IO开销
- 工厂定义中不要使用模型访问器、修改器处理字段,直接生成原生字段值,减少额外方法调用
- 如果对仿真数据的真实度要求不高,可以直接跳过Factory层,在循环内原生拼接字段数组,砍掉Faker生成数据的开销
注意:单批插入条数不要超过2000,否则单条SQL长度很容易触发数据库
max_allowed_packet限制报错,也会导致单次内存峰值过高。
常见误区避坑
- 不要尝试一次性生成100万条数据再写入,无论怎么优化都会触发内存溢出,分批是必须的
- 不要在批量插入场景使用Eloquent的
createMany方法,该方法底层依然是逐条执行插入逻辑,性能和循环单条插入没有本质区别 - 不要在填充过程中开启调试栏、日志打印,额外的日志存储会快速吃满内存
内容的提问来源于stack exchange,提问作者color
相关产品推荐
相关产品推荐

