一、前言
古人云:"一命;二运;三风水;四积功德;五读书。"
回顾过去的一年,公司的故障报告里 防重复问题 反复出现,比如:
- 人民币钱包付款:只付款了一次,但是系统付了两笔,两笔的创单间隔时间是5s
- 领取奖励金重复:重复获得 -> 盗刷
- 疑似重复入账:平台背后的支付机构汇款异常,一笔交易打款两次(导致给客户入账两笔资金)
- 等等
此类问题归类于没有做好幂等,那么幂等是什么?
对同一操作重试任意多次,系统的最终状态,以及对外可观测的结果不变;为容错与重试而生。
- 网络问题 / 前端操作抖动,造成重复提交,后台系统仅处理一次。

针对这类问题,有一个口诀:一锁二判三更新
- 一锁:先对同一业务唯一单号加锁,串行化并发请求,避免同时修改同一份数据。
- 二判:拿到锁后先判定是否已处理过/是否允许处理(幂等判重、状态判定、业务约束)。已处理则直接返回历史结果,不再执行。
- 三更新:要落库幂等记录(带唯一幂等键),重复请求直接命中并返回相同结果。
案例分析:资金安全
以下是3个案例场景。
案例一:防重复扣款(不要轻易换单)
用户下单场景:

- 易错 1:向账户系统,重复扣余额
- 易错 2:重复向渠道扣款
问题:如果扣款 / 扣余额步骤超时了,该怎么办?
即出现了中间态,可以先查询请求情况,再重试。
- 超时关单不可取:可能账务系统那扣款成功了。
Tips:避免重复有如下准则
- 扣钱时:先扣钱再处理业务
- 加钱时:先处理业务再加钱
案例二:防重复入账
海外电商平台入账:平台收款

- 各大银行通过 H2H方式/人工上传,解析入账文件,获得客户的银行入账流水信息
- 根据店铺信息定期拉取账期数据
- 根据亚马逊账单和银行流水,符合匹配规则后,进行入账
存在特殊入账:
- 银行钱先到,账期数据还没到
- 亚马逊 API 授权关闭了,但钱打过来了
- 等等
疑似重复入账:平台背后支付机构汇款异常,一笔交易打款两次,即入金 2 笔。
- 后续银行直接按ACH debit 将资金从账户中扣除
案例三:防重复结算
支付场景是实时的,但要将钱打给商户要通过清结算系统。

- 易错:重复结算、重复打款。
解决:
- 上下游单据一致性核对:支付 - 清结算,单据一致核对(笔数、金额)。
- 打款拦截:打款前规则判断,比如:每日打一次款。
二、实际工作中使用
实际工作中,通常采用组合拳方式:分布式锁 + 数据库兜底
- "分布式锁"实现幂等:因为锁丢失 / 续约失败,仍有重复
- 幂等表:用唯一约束 + 去重表做最终保障
代码如下:
1、分布式锁,一般使用 Redis,封装对应锁工具
java
@RedisLock(lockName = "order", key = "#req.reqId")
public OrderResponse order(Req req) {
// 业务处理
}
2、幂等表
java
public void saveOrder(TradeOrder tradeOrder) {
Order order = (Order) tradeOrder;
transactionTemplate.execute(status -> {
//幂等表
repeatRepository.save(order);
//订单表
orderRepository.saveOrder(order);
});
}