订单只有几千条时,后台直接导出 CSV 很方便;数据增长到几十万条后,同一个操作可能占满 PHP 内存、触发网关超时,甚至拖慢后台其他请求。把执行时间从 30 秒改成 30 分钟,往往只是把问题推迟。

导出前先看列表本身

如果订单 Grid 打开就慢,导出只会更慢。检查筛选条件是否命中索引,第三方模块是否向 Grid 追加多张表 JOIN,以及自定义列是否为每行再次加载订单对象。

EXPLAIN SELECT entity_id, increment_id, created_at, grand_total
FROM sales_order_grid
WHERE created_at >= '2024-04-01 00:00:00'
  AND created_at < '2024-05-01 00:00:00';

示例用于说明思路,实际应从慢查询日志或分析工具获取真实 SQL,不能凭空为每个筛选字段增加索引。

把一次大导出改成明确范围

按月份、Store、订单状态分批导出,比“全部订单”更容易恢复和核对。先在 Grid 中应用筛选,再导出当前集合。业务需要年度数据时,可以在离线环境合并文件。

Grid 数据是否同步

sales_order_grid 是为列表查询准备的扁平表。若第三方代码绕过正常保存流程,Grid 可能缺字段或不同步。应修复写入流程,不要在每次导出时 JOIN 全部 EAV 或业务表补救。

插件是隐藏成本

导出会遍历集合。针对列值的 Plugin、Renderer 或数据提供器如果逐行调用 Repository,就会形成 N+1 查询。100 行列表看不明显,导出 10 万行时会放大为灾难。

真正的大数据导出应该异步

把导出请求写入队列,由 CLI 或消费者分批读取,生成文件后在后台提供下载。这样可以控制批次大小、记录进度、失败重试,并避免 Web 请求长期占用 PHP-FPM。

异步文件应放在受保护的位置,设置过期时间和下载权限。订单 CSV 包含客户信息,不能直接放到公开的 pub/media 路径。

短期处理可以用日期筛选和分批导出;长期方案应把大数据导出从同步后台请求中移走。单纯提高 memory_limit 和超时时间,无法解决数据库和并发压力。