Laravel 5.2批量插入性能优化问题咨询
针对你从server1取数、格式化后批量插入server2时,数据量增大导致插入变慢的问题,我整理了几个实用的优化思路,你可以根据实际情况逐步尝试:
优化Chunk大小与内存管理
你当前设置的30000条的chunk size可能过大,会导致PHP内存占用过高,频繁触发垃圾回收,同时数据库一次性处理大量数据也会增加锁表和IO开销。建议将chunk size调整到5000-10000条(根据服务器和数据库性能灵活调整)。另外,每次处理完一个chunk后,手动清理内存避免泄漏:foreach ($chunks as $chunk) { $formattedData = $this->formatData($chunk); // 直接用DB门面插入,跳过模型实例化 DB::table('rejected_r11s')->insert($formattedData); // 手动清理内存 gc_collect_cycles(); unset($formattedData, $chunk); }跳过模型事件与不必要的特性
如果你用的是Model::insert()方法,本身不会触发模型事件,但如果不小心用了循环create()或者保存模型实例,会触发creating/created等事件,每条记录都要实例化模型,性能损耗极大。一定要坚持用DB::table()->insert()或者Model::insert()的批量插入方式,直接生成原生SQL。
另外,如果你的模型启用了软删除但批量插入不需要,直接用DB门面操作表可以避免额外的deleted_at字段处理,进一步提升效率。跨服务器数据拉取的优化
不要一次性拉取所有数据到内存再拆分chunk,而是分页从server1拉取数据,和插入的chunk对应,减少内存占用:$perPage = 5000; $total = RejectedR11::on('server1')->count(); $pages = ceil($total / $perPage); for ($page = 1; $page <= $pages; $page++) { // 从server1分页取数,直接转数组避免模型实例开销 $data = RejectedR11::on('server1') ->skip(($page - 1) * $perPage) ->take($perPage) ->get() ->toArray(); $formattedData = $this->formatData($data); DB::table('rejected_r11s')->on('server2')->insert($formattedData); gc_collect_cycles(); unset($data, $formattedData); }同时,格式化逻辑尽量简化,避免在循环中做复杂查询或重复计算,用数组操作代替集合/模型操作,进一步降低内存消耗。
数据库层面的关键优化
- 临时关闭索引与外键约束:插入过程中维护索引和外键会大幅增加耗时,插入前临时关闭,完成后再恢复:
// 关闭外键约束 DB::statement('SET FOREIGN_KEY_CHECKS=0;'); // 临时删除二级索引(InnoDB适用,MyISAM可使用ALTER TABLE DISABLE KEYS) DB::statement('ALTER TABLE rejected_r11s DROP INDEX idx_column_name;'); // 执行批量插入... // 重建索引 DB::statement('ALTER TABLE rejected_r11s ADD INDEX idx_column_name(column_name);'); // 开启外键约束 DB::statement('SET FOREIGN_KEY_CHECKS=1;'); - 调整数据库配置:比如MySQL的
innodb_buffer_pool_size(给InnoDB分配更多缓存)、max_allowed_packet(确保能处理大插入语句)、bulk_insert_buffer_size(优化MyISAM批量插入),这些配置调整能从底层提升插入性能。
- 临时关闭索引与外键约束:插入过程中维护索引和外键会大幅增加耗时,插入前临时关闭,完成后再恢复:
尝试直接跨库插入(如果格式化逻辑可SQL实现)
如果你的数据格式化逻辑可以用SQL语句实现,完全可以跳过PHP中转,直接让数据库之间完成数据迁移,性能会提升几个量级:DB::table('rejected_r11s')->on('server2')->insertUsing( ['column1', 'column2', 'formatted_col'], DB::table('rejected_r11s')->on('server1')->select( 'column1', 'column2', DB::raw('CONCAT(column3, "_", column4) as formatted_col') ) );
这些方案可以从易到难逐步尝试,先从调整chunk大小和内存清理入手,再到数据库层面的优化,应该能明显改善插入速度。
内容的提问来源于stack exchange,提问作者Thilagam

