ThinkPHP 百万级数据批量导入需用 CLI 命令分块读取、事务分批入库、异常隔离;禁用 Web 直传,流式读 Excel 每次 500 行,每批≤500 条插入,字段严格清洗校验。

ThinkPHP 做百万级数据批量导入,不能靠 Web 请求直传直处理——会超时、爆内存、锁表、失败难回滚。核心思路是:**分块读取 + CLI 执行 + 事务分批入库 + 异常隔离**。
用 CLI 命令行任务替代 Web 接口
Web 请求通常有 30 秒超时限制,Nginx/Apache/PHP 都可能中断;而百万行 Excel 或 CSV 解析+入库动辄数分钟。必须移出 HTTP 生命周期。
- 通过
php think make:command ExcelImport创建命令类,放在app/command/目录下 - 在
execute()方法中处理上传文件(建议先将用户上传的文件移到runtime/import/下再读) - 开头加
set_time_limit(0)和ini_set('memory_limit', '2G')(CLI 环境下生效) - 可配合 crontab 或队列触发,支持断点续传逻辑(如记录已处理行号)
大 Excel 文件必须流式分块读取
直接 IOFactory::load() 整表加载,10 万行就可能吃光 512MB 内存。要用 PhpSpreadsheet 的迭代器控制内存占用。
- 显式创建读取器:
$reader = IOFactory::createReader('Xlsx'),禁用样式:$reader->setReadDataOnly(true) - 用
getRowIterator($start, $chunkSize)每次只读 500 行($chunkSize = 500是安全阈值) - 每块处理完立刻
unset($row, $cellIterator, $rowData),避免 PHP GC 滞后导致内存持续堆积 - 绝对不要调用
toArray()或getActiveSheet()->getHighestRow()—— 它们会强制加载全量索引
事务包裹 + 分批插入(每批 ≤ 500 条)
单次 insertAll() 插入 10 万条,大概率触发 MySQL 的 max_allowed_packet 错误(默认 4MB),也会拉长锁表时间。
立即学习“PHP免费学习笔记(深入)”;
- 用
array_chunk($chunkData, 400)切片,每批控制在 400~500 条之间 - 每批用
Db::transaction()包裹Db::name('table')->insertAll($batch) - 事务内不做任何外部调用(不发短信、不调 API、不写日志文件),只做 DB 写入
- 捕获
Throwable后重新 throw,确保事务自动回滚;别用try/catch + echo吞掉异常
字段清洗与类型转换要前置校验
用户上传的 Excel 天然不可信:标题含空格换行、数字变科学计数法、日期存为整数、空单元格是 '' 或 null。
- 第一行提取标题后,统一
trim()+ 去 \n\r + 映射到数据库字段(如 “手机号” →mobile) - 数值列用
$cell->getDataType() === Cell::TYPE_NUMERIC判断,再用getFormattedValue()防 1.23E+10 丢精度 - 日期列必须用
Date::excelToDateTimeObject($value)转 PHP DateTime,再格式化为 Y-m-d H:i:s - 空单元格统一转
null(对应数据库允许 NULL 的字段),或按业务设默认值(如status = 0)



















