在订单列表加了一个“渠道”或“外部单号”列,数据量小时正常,上线几个月后打开页面要几十秒。最常见的错误是列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。

