接口幂等性设计:后端数据安全的最后一道防线

80 阅读3分钟

在分布式系统中,接口重复调用是家常便饭 —— 网络抖动导致重试、用户重复提交、网关超时重发等,都可能造成数据重复创建、金额重复扣减等严重问题。接口幂等性设计,就是确保同一请求被多次调用时,结果始终一致的技术手段。

什么是幂等性?

幂等性(Idempotence)源于数学概念,指一个操作执行多次与执行一次的效果相同。在后端接口中,体现在:

  • 查询操作天然幂等(SELECT)
  • 删除操作幂等(DELETE 多次结果一致)
  • 新增、更新操作可能非幂等(如 POST 创建订单、PUT 累加金额)

核心实现方案

1. 唯一 ID 令牌机制(推荐)

这是最通用的方案,流程如下:

  1. 前端请求获取令牌(Token),后端生成唯一 UUID 存入 Redis,设置过期时间(如 5 分钟)

  2. 前端提交业务请求时携带该 Token

  3. 后端校验 Redis 中是否存在该 Token:

    • 存在:执行业务逻辑,删除 Token(确保只能用一次)

    • 不存在:返回 “重复提交” 提示

代码示例

// 获取令牌接口
@GetMapping("/get-token")
public String getToken() {
    String token = UUID.randomUUID().toString();
    redisTemplate.opsForValue().set("order:token:" + token, "1", 5, TimeUnit.MINUTES);
    return token;
}

// 提交订单接口(幂等处理)
@PostMapping("/create-order")
public Result createOrder(@RequestParam String token, @RequestBody Order order) {
    // 1. 校验令牌
    Boolean hasToken = redisTemplate.delete("order:token:" + token);
    if (!hasToken) {
        return Result.fail("请勿重复提交");
    }
    // 2. 执行订单创建逻辑
    orderService.create(order);
    return Result.success();
}

2. 基于业务唯一键去重

利用业务中天然的唯一标识(如订单号、手机号),在执行操作前校验该标识是否已存在:

  • 新增订单时,用订单号作为唯一键,先查库再插入

  • 支付接口用 “订单号 + 用户 ID” 作为唯一键,防止重复支付

SQL 示例

-- 插入时校验,仅当订单号不存在时执行
INSERT INTO orders (order_no, user_id, amount)
SELECT #{orderNo}, #{userId}, #{amount}
WHERE NOT EXISTS (
    SELECT 1 FROM orders WHERE order_no = #{orderNo}
)

3. 乐观锁机制

适用于更新操作,通过版本号控制,确保只有版本匹配时才执行更新:

// 实体类添加版本号字段
public class Product {
    private Long id;
    private Integer stock;
    private Integer version; // 版本号
}

// 更新库存时校验版本
@Update("UPDATE product SET stock = stock - #{num}, version = version + 1 " +
        "WHERE id = #{id} AND version = #{version}")
int deductStock(@Param("id") Long id, @Param("num") int num, @Param("version") int version);

调用时若返回 0,说明版本已变(被其他线程修改),需重试或返回失败。

方案选择策略

  • 高频写操作(如订单创建):优先用唯一 ID 令牌
  • 有天然唯一键的场景(如用户注册):用业务唯一键去重
  • 库存扣减、金额变动:乐观锁 + 事务结合
  • 分布式事务场景:结合 TCC、Saga 等分布式事务框架

注意事项

  • 令牌过期时间需合理设置(太短导致正常请求失败,太长占用 Redis 空间)

  • 并发场景下需用 Redis 的SET NXDELETE原子操作,避免校验逻辑非原子性

  • 非幂等接口需明确标识(如文档中注明 “此接口不保证幂等,请避免重复调用”)

接口幂等性设计不是银弹,但它是后端数据安全的最后一道防线,尤其在支付、订单等核心场景,必须做到 “零容忍”。