t := time.Now().UTC().Truncate(time.Millisecond)
key := fmt.Sprintf("%s-%d", orderNo, t.UnixMilli())
同一笔支付,Go 服务用 time.Now().UnixNano() 生成幂等键,Java 侧按 Instant 的微秒精度重算,两个值差了几百纳秒,补偿任务把订单判成重复。先别急着动参数,把报错原文和时间点记下来,顺序反了容易白折腾。
原因不复杂,UnixNano 是纳秒,但 Java 的 Instant 只保留到微秒,MySQL datetime(6) 再截一次。链路上每一段精度不同,用时间戳拼键就一定会漂。
「跨语言时间精度」排查到最后往往是很小的一处差异,前面那些复杂猜测可以先放一放。备份不是备了就行,恢复一次才算数,很多备份文件打开才发现是空的。
接口问题先分清是没发出去、发出去了没收到,还是收到了处理失败。
处理上按这个顺序:幂等键不要用时间戳 改成业务单号加固定序列;必须用时间就统一到毫秒 并在网关处截断;存储列改成 datetime(3) 或 bigint 存毫秒 别用 datetime(6);回归时造一条带纳秒的时间 从 Go 写到 Java 再读回来对比。
改完只确认一件事:两端算出的键完全一致 补偿任务不再把同一笔订单判成重复。
