保存一个商品需要几十秒,批量更新更容易 504。单纯增加 Nginx/PHP 超时只会让后台 Worker 被占得更久。需要把一次保存拆成数据库写入、插件/Observer、URL Rewrite、索引、缓存失效和外部同步分别计时。

先建立可重复样本

选择一个普通 Simple Product,只改一个不影响库存和价格的文本属性,记录开始时间、结束时间、SKU、管理员和 request ID。再分别测试价格、分类、图片、库存字段,比较哪类修改明显变慢。

curl -sk -w 'TTFB:%{time_starttransfer} TOTAL:%{time_total}
'   -o /dev/null https://admin.example.com/admin/

浏览器 Network 能看到保存 POST 的 Waiting 时间,但后台请求需要真实登录,不能用 curl 伪造 Session。

保存期间观察 MySQL

watch -n 1 "mysql -Nse 'SHOW FULL PROCESSLIST'"
mysql -e 'SHOW ENGINE INNODB STATUSG'

若长期等待 catalog_product、url_rewrite、inventory 或 index 表的锁,找持有事务;若大量相似 SELECT,可能是 Observer 循环加载 Repository 造成 N+1。

列出所有 product save 扩展点

rg "catalog_product_save|ProductRepositoryInterface|afterSave|aroundSave"   app/code vendor/*/*/etc vendor/*/*/Plugin vendor/*/*/Observer

第三方 ERP、搜索、营销模块常在同步 Observer 中调用外部 API。网络超时会直接拖住管理员请求。应把外部同步改成可靠队列,保存事务只写本地 outbox;消费者负责重试和失败告警。

用 Profiler 或 APM 看每段耗时

仅在预发布或受控维护窗口启用 Magento Profiler/APM,复现一次后立即关闭。重点查看 Plugin 链、事件 Observer、SQL 数量、URL Rewrite 生成、图片处理和 cache clean。不要在生产长期输出包含管理员 URL 和商品数据的详细 trace。

分类和 URL Rewrite 规模问题

SELECT COUNT(*) FROM catalog_category_product
WHERE product_id = 1234;

SELECT COUNT(*) FROM url_rewrite
WHERE entity_type='product' AND entity_id=1234;

商品挂在数百分类、启用分类路径 URL 或存在异常重复 rewrite 时,保存会生成大量记录。先修分类模型和冲突数据,不能简单关闭所有 URL Rewrite。

索引模式是否把重活放进请求

bin/magento indexer:show-mode
bin/magento indexer:status

Update on Save 会让管理员等待相关索引;Update by Schedule 可缩短请求,但必须保证 Cron 能及时消费。切换模式前评估价格、库存和搜索更新延迟,不能为速度牺牲业务一致性。

验证优化是否真的有效

对同一 SKU 重复三次,记录 P50/P95 保存时间、SQL 数、外部调用数和锁等待;再测试复杂 Configurable/Bundle 商品。最后确认前台属性、价格、库存、分类 URL 和搜索均正确更新。只把 504 变成 200,不代表保存链路没有丢步骤。