bin/magento indexer:reindex catalog_product_priceSQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry,最危险的做法是马上 truncate 索引表。价格索引本来会重建,删除看似合理,但冲突往往发生在临时索引写入阶段,源数据或自定义 SQL 不修正,下一次仍会失败。

先完整保存错误里的键名和值

错误通常包含冲突值与唯一索引名称。先查看表结构,把拼接值对应回列:

SHOW CREATE TABLE catalog_product_index_priceG
SHOW INDEX FROM catalog_product_index_price;

标准价格索引的唯一维度通常涉及实体、客户组与网站。若冲突表带有 _temp_replica 或扩展前缀,要以实际表结构为准。只复制最后一行“Duplicate entry”会丢掉上方正在执行的 INSERT SELECT,排查前应保留完整异常。

确认是不是两个重建同时进行

部署脚本、Cron、管理员手动命令可能同时触发全量索引。检查进程和数据库活动,不要在未确认的情况下 kill 线程:

ps -ef | grep '[m]agento indexer'
mysql -e 'SHOW FULL PROCESSLIST' | grep -i catalog_product

若确有并发重建,先停止重复入口并等待当前事务结束,再进行一次受控重建。不要把数据库锁等待误判为重复数据。

从生成 SQL 追到源数据

冲突常见于自定义模块 join 了一张一对多表,却没有聚合,导致同一实体、网站、客户组组合产生两行。搜索对价格 indexer、table strategy、dimension collection 和 price modifier 的 preference/plugin。最近安装模块后才出现问题,先在测试环境禁用该模块对比,而不是在生产环境直接改 vendor 代码。

如果错误指向目录规则临时表,检查规则与网站、客户组关联;指向可配置或捆绑价格表,则检查父子关联是否重复。可先用唯一维度分组找出重复候选:

SELECT entity_id, customer_group_id, website_id, COUNT(*) AS rows_count
FROM suspected_source_table
GROUP BY entity_id, customer_group_id, website_id
HAVING COUNT(*) > 1
ORDER BY rows_count DESC;

suspected_source_table 必须替换为错误堆栈中实际的源表。不要把这段示例直接对未知表执行删除。

检查实体关系,而不是只看产品 SKU

重复 SKU 与价格索引的冲突键不是同一回事。应检查 catalog_product_relation、super link、website assignment,以及导入程序是否重复插入关联。标准表通常有唯一约束保护,绕过 service contract 的旧脚本可能留下异常关系。

清理必须有证据和回滚

确定是无效重复关系后,先备份目标行,记录实体 ID 与业务含义,再通过模块数据补丁或可审计 SQL 修复。修复源数据后依次运行 setup:db:status、价格索引和缓存清理。若只是索引临时表残留,也应先确认没有索引进程,再按当前版本的索引表交换机制处理。

完成标准不是命令这一次显示成功,而是再保存一个相关商品、等待增量索引、检查各客户组与网站价格都正确。这样才能证明唯一键冲突的来源已经消失,而不是被一次全量重建暂时覆盖。