支付防重复下单|5 种幂等性设计方案(从初级到架构级)

核心概念:什么是幂等性

同一个请求无论执行多少次,最终结果完全一致。典型场景:用户支付时网络卡顿重复点击按钮,后台接收多个请求,仅扣一次钱、生成一个订单。

5 种幂等性设计方案(附优缺点 + 适用场景)

方案 1:数据库唯一索引【初级|最简单粗暴】

实现 :订单表对订单号加唯一索引,插入重复订单号时数据库直接报错,后端捕获异常并返回「订单已存在」。

优点:实现简单、数据库天然支持,无额外代码开发;

缺点:仅防插入重复,无法处理更新操作的幂等;抛异常处理,性能稍差;

适用场景:订单创建、用户注册、商品上架等纯插入操作。

方案 2:Token 机制【中级|防前端重复提交】

实现

①用户打开支付页,后端生成唯一 Token 存入 Redis(设 5 分钟过期)并返回前端;

②前端提交支付请求时携带 Token;

③后端通过Lua 脚本原子性完成「Redis 查 Token + 存在则删除」,删除成功则处理业务,否则返回「请勿重复提交」。

核心:查询 + 删除必须原子性,避免高并发下的并发问题;

优点:通用性极强,可适配绝大多数场景;

缺点:需要前端配合传参;强依赖 Redis,Redis 宕机则失效;

适用场景:表单提交、评论发布、支付发起等需要前端配合防用户重复点击的场景。

方案 3:分布式锁【中级|高并发防重复】

实现 :以订单号 / 用户 ID + 业务标识为锁的 Key,基于 Redis 实现分布式锁;

①加锁时生成唯一标识 + 设置 30 秒过期时间;

②加锁失败则返回「操作进行中,请稍后」;

③加锁成功处理业务,处理完成后通过 Lua 脚本判断并删除自身持有的锁;

核心:规避分布式锁 3 大坑(过期时间、误删别人的锁、锁释放);

优点:通用性强,能有效防高并发重复请求;

缺点:性能稍差,依赖 Redis;

适用场景 :支付扣钱、库存扣减、优惠券领取等需要严格防并发的操作。

方案 4:状态机 + 乐观锁【高级|最优解】

实现:基于业务状态流转实现,如订单状态:待支付→支付中→已支付 / 支付失败

①支付请求到达后,先判断订单当前状态;

②通过数据库乐观锁原子性更新状态(SQLWhere 条件带「当前状态 + 版本号」),仅当状态为「待支付」且版本号匹配时更新为「支付中」;

③更新成功则处理业务,失败则返回「订单已处理」。

核心:状态流转的原子性,由数据库乐观锁保证;

优点:不依赖任何外部组件,逻辑清晰贴合业务语义,性能优;

缺点 :仅适用于有明确状态流转的业务;

适用场景:订单支付、工单处理、审批流、物流状态更新等有固定状态的业务。

方案 5:全局唯一 Request ID【架构级|最严谨】

实现

①前端 / 网关生成全局唯一 Request ID,所有请求均携带该 ID;

②后端收到请求后,通过 Redis 的setnx 命令原子性标记「该 ID 正在处理」;

③标记失败则返回「请求处理中」,标记成功则处理业务;

④处理完成后将结果缓存至 Redis,后续重复请求直接返回缓存结果;

核心:setnx 天然保证原子性,仅第一个请求能标记成功;需做好结果缓存和过期策略;

优点 :最严谨,适配所有场景的幂等性,可返回历史处理结果;

缺点:实现复杂,需开发结果缓存逻辑;要求 Request ID 全局唯一且无冲突;

适用场景 :支付转账、资金划拨、交易清算等核心高敏感业务(对幂等性要求极致的场景)。

方案选择指南(快速匹配业务)

  1. 纯插入操作→数据库唯一索引(最简单);
  2. 前端用户重复点击→Token 机制(适配性强);
  3. 高并发防重复处理→分布式锁(性能与可靠性平衡);
  4. 有状态流转的业务→状态机 + 乐观锁(最优雅,贴合业务);
  5. 核心高敏感业务→全局唯一 Request ID(最严谨,零容错)。

必避坑点(面试高频)

  1. 分布式锁:必须设置合理过期时间,通过 Lua 脚本保证「删锁原子性」,防止误删其他请求的锁;
  2. Token 机制:查询 + 删除必须原子性,严禁分两步操作,避免高并发下重复处理;
  3. 状态机:必须用乐观锁(版本号 / 当前状态) 保证状态更新原子性,杜绝多请求同时修改状态;
  4. 全局 Request ID:做好缓存结果的过期策略,避免 Redis 内存溢出;保证 Request ID 的全局唯一性(如雪花算法)。

面试加分项

1. 结合实战案例回答

例:公司支付接口采用「Token 机制 + 状态机 + 唯一索引」组合方案:

①前端通过 Token 防重复点击;

②订单表以订单号做唯一索引,防重复插入;

③通过状态机 + 乐观锁控制支付状态流转,确保仅一次支付成功,上线后彻底解决重复下单问题。

2. 延伸 MQ 消息幂等性

MQ 消息可能因网络问题重复投递,消费者需保证幂等:

  • 方案 1:基于消息 ID 做 Redis 去重,消费前先判断消息 ID 是否处理过;
  • 方案 2:业务层天然幂等,如扣减库存时 SQL 加条件「where 库存 > 0」,重复扣减时直接失败。

3. 核心总结

幂等性是分布式系统的基本功,并非简单加锁即可解决;实际开发中需结合业务场景组合方案 ,而非单一使用,核心原则是:保证请求处理的原子性,杜绝重复执行带来的脏数据

相关推荐
日月云棠1 天前
各版本JDK对比:JDK 25 特性详解
java
兆子龙1 天前
用 React + Remotion 做视频:入门与 AI 驱动生成
前端·架构
用户8307196840821 天前
Spring Boot 项目中日期处理的最佳实践
java·spring boot
JavaGuide1 天前
Claude Opus 4.6 真的用不起了!我换成了国产 M2.5,实测真香!!
java·spring·ai·claude code
IT探险家1 天前
Java 基本数据类型:8 种原始类型 + 数组 + 6 个新手必踩的坑
java
花花无缺1 天前
搞懂new 关键字(构造函数)和 .builder() 模式(建造者模式)创建对象
java
用户908324602731 天前
Spring Boot + MyBatis-Plus 多租户实战:从数据隔离到权限控制的完整方案
java·后端
桦说编程1 天前
实战分析 ConcurrentHashMap.computeIfAbsent 的锁冲突问题
java·后端·性能优化
一枚前端小姐姐1 天前
低代码平台表单设计系统技术分析(实战二)
低代码·架构·前端框架
爱勇宝1 天前
2026年前端生存规划:只会写页面的人,正在被悄悄淘汰
前端·后端·架构