在订单列表加了一个“渠道”或“外部单号”列,数据量小时正常,上线几个月后打开页面要几十秒。最常见的错误是列Renderer里按每个订单调用Repository,20行就发出20到60条SQL;其次是把大表JOIN进Grid却没有索引。

先确认慢在哪里

bin/magento dev:query-log:enable
# 复现一次后立即关闭
bin/magento dev:query-log:disable

tail -n 200 var/debug/db.log

生产环境不要长期开query log。也可以用MySQL slow_query_log短时采样。找到Grid主SQL后执行EXPLAIN,重点看type=ALL、rows过大、Using temporary和Using filesort。

三种改法,优先级从上到下

把展示字段同步到sales_order_grid

如果字段随订单创建后很少变化,这是最稳的方案。为sales_order_grid增加列,在订单保存或异步同步时写入,UI Component直接读取,不再运行时JOIN。已有订单用分批脚本回填,每批提交,避免长事务。

必须JOIN时补复合索引

EXPLAIN SELECT ...;
SHOW INDEX FROM vendor_order_meta;
ALTER TABLE vendor_order_meta ADD INDEX IDX_ORDER_CHANNEL (order_id, channel_code);

索引列顺序要匹配JOIN与筛选条件,不要看到慢就给每列单独加索引。先在预生产用接近生产的数据量验证写入成本和执行计划。

移除Renderer中的对象加载

Renderer只使用当前row已经提供的数据。不要在 prepareDataSource() 循环里调用OrderRepository、CustomerRepository或外部API。需要多值时一次收集ID、批量查询,再按ID映射。

筛选和排序也要单独测试

  • 无筛选打开第一页;
  • 按新列精确筛选;
  • 按新列排序;
  • 组合日期范围与状态筛选;
  • 导出CSV,因为导出会扫描更多行。

优化后用同一SQL再次EXPLAIN,对比扫描行数与实际耗时。若字段来自实时外部系统,把同步状态落库展示,不要让后台Grid请求等待外部API。