一、客户端侧方案
唯一请求标识(Request ID)
- 客户端在每次请求时生成全局唯一的请求ID(如UUID、时间戳+随机数),并在请求头或参数中携带。
- 服务端通过该ID识别重复请求,若已处理过相同ID的请求,直接返回之前的结果。
防重提交交互设计
- 按钮防抖:提交后禁用按钮,避免用户多次点击(如支付、下单场景)。
- 加载状态:提交后显示加载动画,阻止用户继续操作。
- 页面跳转:提交后重定向到结果页,避免浏览器回退重复提交。
合理重试策略
- 对于超时或失败的请求,客户端重试时应携带相同的请求ID,而非生成新请求。
- 避免无限制重试,可设置最大重试次数或指数退避策略。
二、服务端侧方案
唯一索引约束
- 对数据库表中业务唯一字段(如订单号、流水号)添加唯一索引,天然拦截重复数据。
- 示例:订单表的
order_no字段加唯一索引,重复插入直接报错。
Token 机制
流程:
- 客户端先请求服务端获取一个一次性Token(如通过Redis存储并设置过期时间)。
- 提交业务请求时携带此Token。
- 服务端校验Token是否存在:存在则处理请求并删除Token;不存在则拒绝。
- 适用场景:表单提交、支付接口。
状态机校验
- 业务数据增加状态字段(如订单状态:未支付、已支付、已完成),处理请求前校验状态是否允许变更。
- 示例:已支付的订单再次收到支付请求时,直接返回成功,不重复扣款。
分布式锁控制
- 对关键业务操作(如库存扣减)加分布式锁(如Redis的
SETNX或 RedLock),确保同一资源在同一时间仅被处理一次。 - 锁的Key可使用业务ID(如商品ID+用户ID)或请求ID。
- 对关键业务操作(如库存扣减)加分布式锁(如Redis的
幂等表/日志表
- 单独建立幂等表,记录已处理的请求ID和业务状态。
- 处理请求前先插入幂等表(利用唯一索引防重),成功后再执行业务逻辑。
乐观锁
更新数据时基于版本号或时间戳:
UPDATE table SET amount = 100, version = version + 1 WHERE id = 123 AND version = 当前版本;- 若更新影响行数为0,说明数据已被修改,拒绝重复操作。
三、业务场景适配
- 读操作(GET):天然幂等,无需处理。
写操作(POST/PUT/DELETE):需根据业务选择上述方案组合。
- 创建资源(POST):Token机制 + 唯一索引。
- 更新资源(PUT):乐观锁 + 状态机。
- 删除资源(DELETE):多次删除返回相同结果即可。
四、注意事项
全局唯一ID生成
- 确保请求ID或业务ID全局唯一(如雪花算法、Redis自增ID、UUID)。
安全与防篡改
- Token或请求ID需加密传输,防止被恶意截获重用。
结果一致性
- 对重复请求应返回与第一次相同的响应(如订单ID、支付结果)。
清理机制
- 定期清理幂等表或Redis中的过期数据,避免存储膨胀。