自定义列表显示 20 个商品,分页器却说有 80 个,或 getSize 比 getItems 数量少。加入多值属性、分类、库存或自定义一对多表后,一个 product_id 可能扩成多行,而集合计数 SQL 与加载 SQL 的 DISTINCT/GROUP 处理不同。

固定复现条件

固定筛选条件,分别打印脱敏后的 select SQL、getSelectCountSql、getSize 和已加载 item ID。先移除自定义 join 对照。

grep -R --line-number 'joinLeft|joinField|addFieldToFilter|getSelectCountSql' app/code/Vendor 2>/dev/null
mysql -e "SELECT product_id,COUNT(*) FROM custom_relation GROUP BY product_id HAVING COUNT(*)>1 LIMIT 20"
# 开发环境记录:collection->getSelect() 与 collection->getSelectCountSql()
php bin/magento indexer:status

根据证据定位

一对多 join 未按 product_id 聚合会重复;在加载后才加过滤条件会留下已缓存 size;GROUP BY 与 COUNT(*) 组合可能统计分组数错误。盲目 distinct(true) 有时会掩盖列设计问题并拖慢查询。

修复与回滚

先把关系表聚合为每商品一行,或使用 EXISTS 子查询表达过滤;必要时重写受控的 count SQL。所有过滤与 join 应在调用 getSize/load 前完成,避免依赖集合内部缓存。

验收

无关系、一条关系、多条关系、分页边界都测试;getSize 等于唯一商品数,遍历全部页无重复遗漏,EXPLAIN 仍使用合理索引。

生产环境操作前的检查

处理“Magento 2 商品集合 getSize 数量不准确”前,先记录 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 重复验证。临时调试日志必须脱敏,确认错误率、响应时间和数据量稳定后及时关闭。