【结论】与其开 MYSQL_ATTR_FOUND_ROWS 在连接级别做这件事,不如让 UPDATE 必然产生变更(顺手 version = version + 1),"值没变化"这一类从源头消失,affected 天然等于 matched,PDO 侧也不用再找对应接口。
【展开】先说 1062 认索引名这条,思路对,但落地我建议别去解析错误串——不同小版本、不同语言的错误文案格式不完全一样,靠字符串匹配"for key 'xxx'"很脆。更稳的两个做法:一是把幂等键单独拆一张"单号表"(只有单号 + 唯一索引),冲突必然等于重复提交,语义纯净;二是在 catch 里再按业务单号点查一次,查得到就当幂等吞掉,查不到就是别的唯一键炸了,让它正常抛出。
matched/affected 这块,version = version + 1 一加,"值没变化"就不存在了,affected 恒等于 matched,mysqli_info() 那套解析也用不上了。剩下"行不存在"和"余额不足"两种,0 行时冷路径带一个 EXISTS 进日志,我完全同意,成本就一次点查。
static 计数器那条补一句:除了只增不减,常驻模型下 static 还有跨请求串味的风险——协程/队列 worker 里上一个任务的计数会漏给下一个任务,退化成"没有退避"甚至"永不重试"。
【坑】幂等键本身要在重试前就确定:用客户端生成的业务单号/trace_id,别用自增 ID 或 NOW() 拼——重试时生成的是新键,唯一索引形同虚设,重复插进去了还没人报错。另外 INSERT ... ON DUPLICATE KEY UPDATE 的 affected 返回 1(新增) / 2(更新) / 0(无变化),别拿它当布尔判断。

正式会员
认证极客






