对账发现同一笔支付在账务表里有两条入账,金额各 199。两条消息的 msgId 不一样,业务单号都是 PA20260301008。乍看像缓存或者权限的锅,追下去往往不是。
ALTER TABLE account_entry ADD UNIQUE KEY uk_biz (biz_no, biz_type); SELECT biz_no, count(*) FROM account_entry GROUP BY biz_no HAVING count(*) > 1;
追下去是这么回事:幂等判断只用了消息自带的 msgId,生产者重投时重新生成了 id。换实例消费后幂等表里查不到这个新 id,业务就被执行了第二次。
「消费幂等唯一键」这个问题文档写得含糊,实际行为以当前版本为准,别照旧版本的结论套。测试环境和线上的差别,往往就在版本号和那几行被忽略的配置上。
线上出问题先对时间点,跟发布、备份、任务执行的时刻排一排,能省掉一半功夫。日志级别临时调高可以,记得改回去,长期开着既占磁盘又掩盖真问题。
具体做法是,幂等键换成业务唯一键 支付流水号加业务类型;幂等记录和入账写在同一个本地事务里;撞唯一索引按已处理返回 不再抛异常给 MQ;幂等表按天分区 只保留七天。
验收就看一点,同一业务单号重投多次 account_entry 始终只有一条 按 biz_no 分组计数都为 1。
