前端按 currentPage 加载商品,第二页重复第一页商品或少了部分 SKU。Offset 分页要求排序在整个请求期间稳定;大量商品具有相同价格、位置或时间时,如果没有稳定的次级排序,数据库或搜索引擎可能改变同值记录顺序。

先固定复现条件和影响范围

保存每页请求变量、返回 SKU 顺序、total_count、索引别名和请求时间。固定分类与筛选条件,连续请求相同页两次,并在无商品更新的窗口复现。

curl -sS -H 'Content-Type: application/json'  --data '{"query":"query($p:Int!){products(search:"",pageSize:20,currentPage:$p,sort:{price:ASC}){total_count items{sku name}}}","variables":{"p":1}}'  https://shop.example.com/graphql
grep -R --line-number 'setOrder|addOrder|sort' app/code/*/*/Model/Resolver 2>/dev/null
php bin/magento indexer:status catalogsearch_fulltext catalog_product_price

根据证据分支定位

相同请求自己就顺序变化,优先查非稳定排序和搜索集群;固定不变但页间重复,检查 offset、pageSize 和自定义结果拼接;只有索引更新时变化,则评估无限滚动期间商品变更的产品体验。自定义 Resolver 若先分页后再过滤,会必然漏项。

修复时保留回滚路径

使用业务排序字段加稳定且唯一的次级键,过滤应在分页之前完成。客户端对短时数据变更可按 SKU 去重,但不能用它掩盖服务端 total_count 和排序错误;导出等强一致任务改用主键游标分批。

上线验收

准备多个相同价格与位置的商品,遍历全部页后 SKU 集合应无重复且数量等于 total_count。索引更新、商品上下架和不同 Store View 下也要有可解释的一致行为。

生产环境操作前的检查

处理“Magento 2 GraphQL 翻页出现重复或漏商品”前,先记录 Magento 版本、部署模式、问题 Store View、复现时间和最近一次发布。所有 SQL 默认先执行 SELECT;任何写操作、目录清理或配置切换都应确认影响范围、保留备份并准备回滚。多节点环境要核对各节点代码版本、app/etc/env.php 配置摘要与流量分布。

php bin/magento --version
php bin/magento deploy:mode:show
php bin/magento maintenance:status
php bin/magento indexer:status
php bin/magento cache:status

命令应由 Magento 文件所有者在项目根目录执行。不要在生产高峰同时运行全量索引、静态部署和数据修复;涉及客户、支付、税费或订单的改动,先在脱敏数据副本验证。

建立可比较的排查记录

每次实验只改变一个变量,记录操作前值、命令、开始与结束时间、结果和回滚方式。准备一个正常对象与一个异常对象对照;若旧对象恢复而新对象仍能复现,说明根因尚未消除。

检查层需要保存的证据通过标准
入口层 URL、状态码、请求 ID、节点 路由和协议一致
应用层 异常堆栈、模块、作用域 无新异常且可重复
数据层 主键、时间、关联行数 关系完整且无重复副作用
业务层 正常、失败、重试路径 最终结果一致

修复后的观察窗口

上线后至少观察一轮 Cron、队列和索引周期,并在两台节点、无痕浏览器和目标 Store View 重复验证。临时调试日志必须脱敏,确认错误率、响应时间和数据量稳定后及时关闭。