importer类本身不处理并发,仅定义单批次导入规则;真正并发需外部调度+分片+独立进程,配合withchunkreading和withbatchinserts实现安全高效批量处理。

Importer 类本身不处理并发,它只是定义导入规则和流程的载体。真正实现并发导入,得靠外部调度 + 分片 + 独立进程,Importer 只负责“单批次”安全、可复用的解析与写入。
Importer 必须实现 WithChunkReading 和 WithBatchInserts
不加
WithChunkReading:默认会把整个 Excel 文件一次性读进内存,10 万行就可能爆到 200MB+,根本撑不住并发不加
WithBatchInserts:哪怕开了分片,每行仍走save(),10 万次 insert = 10 万次 SQL 执行 + 连接开销,I/O 成瓶颈正确组合下,单个
Importer实例能稳定处理 5k~10k 行/批,配合外部并发调度,才能线性提升吞吐batchSize()建议设为 500~1000,超过 2000 容易触发 MySQL 的max_allowed_packet或锁表chunkSize()(来自WithChunkReading)建议与batchSize()一致,避免内存中堆积未插入数据必须显式关闭模型事件:在
model()方法里别调->save(),改用collect(...)->toArray()交给批量插入
并发不能靠 dispatch(new ImportJob) 直接塞 N 个文件
多个
ImportJob同时跑,若共用同一张表且没做主键隔离,ChunkById会因min(id)/max(id)快照失效而漏/重更危险的是事务:每个 job 开
DB::transaction()包住整个导入流程?内存没炸,死锁先来了正确做法是预分片:用
php artisan tinker先算好每个 job 处理的 ID 范围,或按文件内 sheet 名/列哈希分桶每个并发 job 应只处理一个独立数据子集(如
sheet_1、user_status=active、id BETWEEN 10001 AND 20000)所有 job 共享同一个
Importer类,但构造时传入不同$range或$sheetName避免在
import()内部查库——筛选逻辑必须前置,job 只做“纯导入”
导入失败后状态不可靠,fail() 回调不能依赖数据库字段
Importer的failures()方法返回的是内存里的错误行,不是 DB 记录;如果中间被 kill 或超时,这部分数据就丢了不要指望
failed_jobs表里存了完整上下文——它只记异常堆栈,不存原始 Excel 行内容真实生产环境必须自己落日志:在
onFailure()里写storage_path('logs/import-'.date('Y-m-d').'.log'),把$failure->row()和$failure->values()记全错误行导出要用
FastExcel单独生成失败报告,别用Excel::download()——后者依赖 Laravel 请求生命周期,job 里不可用WithValidation的校验结果不会自动进failures(),得手动 throwValidationException才触发回调
最常被忽略的一点:并发导入 ≠ 并发执行,而是并发「准备 + 分发」。真正的导入动作仍应尽量串行化或强隔离,否则数据一致性比性能更难兜底。











