一、客户端侧方案

  1. 唯一请求标识(Request ID)

    • 客户端在每次请求时生成全局唯一的请求ID(如UUID、时间戳+随机数),并在请求头或参数中携带。
    • 服务端通过该ID识别重复请求,若已处理过相同ID的请求,直接返回之前的结果。
  2. 防重提交交互设计

    • 按钮防抖:提交后禁用按钮,避免用户多次点击(如支付、下单场景)。
    • 加载状态:提交后显示加载动画,阻止用户继续操作。
    • 页面跳转:提交后重定向到结果页,避免浏览器回退重复提交。
  3. 合理重试策略

    • 对于超时或失败的请求,客户端重试时应携带相同的请求ID,而非生成新请求。
    • 避免无限制重试,可设置最大重试次数或指数退避策略。

二、服务端侧方案

  1. 唯一索引约束

    • 对数据库表中业务唯一字段(如订单号、流水号)添加唯一索引,天然拦截重复数据。
    • 示例:订单表的 order_no 字段加唯一索引,重复插入直接报错。
  2. Token 机制

    • 流程

      1. 客户端先请求服务端获取一个一次性Token(如通过Redis存储并设置过期时间)。
      2. 提交业务请求时携带此Token。
      3. 服务端校验Token是否存在:存在则处理请求并删除Token;不存在则拒绝。
    • 适用场景:表单提交、支付接口。
  3. 状态机校验

    • 业务数据增加状态字段(如订单状态:未支付、已支付、已完成),处理请求前校验状态是否允许变更。
    • 示例:已支付的订单再次收到支付请求时,直接返回成功,不重复扣款。
  4. 分布式锁控制

    • 对关键业务操作(如库存扣减)加分布式锁(如Redis的 SETNX 或 RedLock),确保同一资源在同一时间仅被处理一次。
    • 锁的Key可使用业务ID(如商品ID+用户ID)或请求ID。
  5. 幂等表/日志表

    • 单独建立幂等表,记录已处理的请求ID和业务状态。
    • 处理请求前先插入幂等表(利用唯一索引防重),成功后再执行业务逻辑。
  6. 乐观锁

    • 更新数据时基于版本号或时间戳:

      UPDATE table SET amount = 100, version = version + 1 
      WHERE id = 123 AND version = 当前版本;
    • 若更新影响行数为0,说明数据已被修改,拒绝重复操作。

三、业务场景适配

  • 读操作(GET):天然幂等,无需处理。
  • 写操作(POST/PUT/DELETE):需根据业务选择上述方案组合。

    • 创建资源(POST):Token机制 + 唯一索引。
    • 更新资源(PUT):乐观锁 + 状态机。
    • 删除资源(DELETE):多次删除返回相同结果即可。

四、注意事项

  1. 全局唯一ID生成

    • 确保请求ID或业务ID全局唯一(如雪花算法、Redis自增ID、UUID)。
  2. 安全与防篡改

    • Token或请求ID需加密传输,防止被恶意截获重用。
  3. 结果一致性

    • 对重复请求应返回与第一次相同的响应(如订单ID、支付结果)。
  4. 清理机制

    • 定期清理幂等表或Redis中的过期数据,避免存储膨胀。
最后修改:2025 年 03 月 25 日
如果觉得我的文章对你有用,请随意赞赏