后端 设计分布式

接口幂等:从一次重复扣款说起

对账时发现一笔订单扣了两次款。查日志:支付回调超时,支付平台重发了一次,而我们的回调接口没有防重。这不是 bug,是假设错了——默认每条请求只会来一次,而网络世界里重试才是常态。

幂等的思路很统一:给请求一个全局唯一的身份,然后保证"同一个身份只生效一次"。落到实现,常用三招:

整体链路示意
整体链路示意
最容易踩的坑:先 SELECT 再 INSERT。两个并发请求都查到"不存在",然后都插入成功。防重必须靠原子操作(唯一索引、SET NX、条件更新),不能靠"先查一下"。

另一个经验是防重范围要对齐业务。支付回调防重用支付流水号,前端表单防重用页面一次性令牌——拿别人的单号防自己的重试,等于没防。做设计评审时我只问一个问题:"这条请求的唯一身份是什么?两个相同的请求会带一样的身份吗?"答案清楚,方案就清楚。

那次重复扣款之后,所有回调入口统一加了防重表,顺手把"重试也安全"写进了接口设计模板的第一条。