同一个 Magento 2 GraphQL 接口上线初期很快,商品量或前端功能增加后逐渐变慢,通常不是 GraphQL 协议本身的问题。真正耗时来自查询请求了什么字段、每个 Resolver 如何取数、是否发生 N+1 查询,以及响应能否被正确缓存。

先固定查询与变量再测量

不要只说“products 查询慢”。保存完整 operation name、query、variables、商店头和响应大小,用相同条件测试。分类第一页请求 20 个商品,与一次请求 200 个商品并展开媒体、价格、库存和自定义属性,负载完全不同。

curl -sS -X POST \
  -H 'Content-Type: application/json' \
  -H 'Store: default' \
  --data @query.json \
  'GraphQL接口地址' -o response.json

分别记录 TTFB、总时间、返回字节数和服务端执行时间。只看浏览器瀑布图容易把网络下载与后端计算混在一起。

自定义 Resolver 最容易产生 N+1

列表返回 50 个商品,如果自定义字段 Resolver 对每个商品各调用一次 repository 或执行 SQL,就会产生几十到几百次重复查询。优先使用批量 Resolver、Data Provider 或一次集合查询,并按实体 ID 批量加载所需字段。

不要在 Resolver 中调用 ObjectManager、重复加载完整 Product 模型,或为每个字段触发外部 HTTP 请求。外部数据应有明确超时、批量接口和缓存策略。应用性能监控中如果同一 SQL 只替换 entity_id 重复出现,就是典型信号。

检查查询选择集是否过大

GraphQL 的优势是客户端选择字段,但这也意味着客户端能请求昂贵的嵌套结构。前端页面只展示名称和价格,却同时请求 description、所有媒体、reviews、options 和大量自定义属性,会增加数据库、搜索服务与序列化成本。拆分首屏必需字段和延迟加载字段,通常比服务器盲目加资源有效。

分页也要合理。极大的 pageSize 会放大商品加载与响应体;深页 offset 查询可能越来越慢,具体策略要结合 Magento 当前接口能力和业务场景。

缓存命中依赖身份与上下文

匿名查询与登录客户查询的缓存条件不同。价格、客户组、Store、Currency 和授权头都会影响结果。自定义 Resolver 如果没有返回正确的 cache identity,可能出现不能缓存或缓存串数据。先验证响应头和 Magento GraphQL resolver result cache 状态,再决定 CDN 是否安全缓存。

SQL、搜索与 PHP 都要有证据

对一次慢请求抓取 SQL 数量与累计耗时、OpenSearch 查询耗时、PHP CPU 和内存。数据库慢时检查集合过滤和索引;搜索慢时检查聚合字段与分片;PHP 时间高则检查 Resolver 循环和序列化。不要把所有慢查询都归因于 Redis。

优化后用相同 query/variables 做冷缓存与热缓存对比,并增加一个较大分类和一个登录客户场景。除平均值外还要观察 P95/P99,因为偶发外部调用或锁等待往往不会体现在平均响应时间中。