分布式场景下接口幂等性保证方案

幂等性 :指同一个请求执行一次和执行多次的结果完全一致,不会因为重复调用产生脏数据、重复扣款、重复下单等问题。

分布式系统中,网络超时、重试机制、前端重复提交、消息重复消费都会导致接口被重复调用,必须强制保证幂等性

我会从核心原理、适用场景、实现方案、最佳实践四个维度,给你最完整、可落地的总结。


一、先明确:哪些接口必须做幂等?

非幂等接口:执行多次会改变数据状态

  • 新增接口(insert)
  • 扣款/扣库存/支付接口
  • 提交订单接口
  • 发送消息/推送接口

天然幂等接口:执行多次无影响

  • 查询接口(select)
  • 修改指定状态的接口(set status=1
  • 删除指定ID接口(delete where id=1

二、分布式幂等性 6 种主流实现方案(按推荐优先级排序)

1. 唯一索引 / 唯一约束(最简单、最推荐)

核心思想 :利用数据库唯一索引天然防重复,重复插入会直接报错,业务捕获异常即可。

适用场景

  • 用户注册(手机号唯一)
  • 订单创建(订单号唯一)
  • 日志/流水表插入

实现步骤

  1. 给业务唯一字段(订单号、手机号、业务编号)建唯一索引
  2. 插入时捕获 DuplicateKeyException
  3. 捕获后直接返回成功,视为幂等处理

优点 :实现最简单、无侵入、可靠性高

缺点:只适用于插入场景,不适合更新/扣款场景


2. 分布式锁(Redis / Zookeeper)

核心思想 :重复请求进来时,只有一个能拿到锁执行,其他请求直接拦截

适用场景

  • 高并发秒杀、库存扣减
  • 支付、退款
  • 所有需要强防重的写操作

实现步骤(Redis 最常用)

  1. 请求携带唯一标识(订单号、用户ID+业务号)
  2. 先尝试 SETNX 加锁(设置过期时间)
  3. 加锁成功 → 执行业务
  4. 加锁失败 → 直接返回"重复请求"/幂等成功

关键 :必须用 Redis 原子操作,防止锁超时、死锁。

优点 :适用范围极广、性能好

缺点:需要引入 Redis,有锁超时风险


3. 前端 Token 机制(最通用、企业标准方案)

核心思想先获取 Token,再用 Token 请求,Token 一次性有效。

适用场景

  • 表单提交(订单、支付)
  • 前端防重复点击 + 后端防重复提交

完整流程

  1. 进入页面时,前端请求后端生成全局唯一 Token(存在 Redis)
  2. 页面渲染时把 Token 隐藏带入表单
  3. 提交接口时,必须携带 Token
  4. 后端使用 Redis DEL 原子操作删除 Token
    • 删除成功 → 放行执行业务
    • 删除失败 → 说明重复请求,直接返回

为什么用 DEL?

  • 原子操作,绝对保证只执行一次
  • 比 GET + DELETE 更安全,不会出现并发问题

优点 :覆盖全场景、无业务侵入、最安全

缺点:多一次请求获取 Token


4. 状态机幂等(业务状态流转控制)

核心思想只有当前状态允许,才能流转到下一个状态,重复请求会被状态拦截。

适用场景

  • 订单流程(待支付 → 已支付 → 已发货)
  • 审批流程
  • 所有有明确状态流转的业务

示例 SQL

sql 复制代码
UPDATE order 
SET status = 2 
WHERE id = ? AND status = 1;
  • 状态=1(待支付)才能改成 2(已支付)
  • 重复执行时,条件不满足,更新行数=0,直接返回

优点 :业务天然幂等、无额外组件依赖

缺点:只适用于有状态流转的业务


5. 悲观锁 / 乐观锁

(1)悲观锁(数据库锁)

sql 复制代码
SELECT * FROM table WHERE id=? FOR UPDATE;
  • 同一数据同一时间只能一个请求操作
  • 适用于低并发、强一致性场景
  • 不推荐分布式高并发

(2)乐观锁(版本号机制)

sql 复制代码
UPDATE table 
SET amount=amount-1, version=version+1 
WHERE id=? AND version=?;
  • 版本号匹配才执行
  • 高并发性能好
  • 适用于扣款、库存更新

6. 全局唯一请求号(上游传入)

核心思想 :上游调用方传入全局唯一 requestId,后端存储并校验。

适用场景

  • 微服务之间调用
  • 第三方回调(支付回调)
  • MQ 消息消费

实现

  • 用 requestId 作为唯一键存入 Redis/DB
  • 存在则直接返回,不存在则执行

三、方案选型速查表(直接照抄用)

方案 适用场景 优点 缺点 推荐指数
唯一索引 插入、防重复数据 简单、可靠 仅插入 ⭐⭐⭐⭐⭐
Token 机制 表单提交、订单、支付 通用、安全 多一次请求 ⭐⭐⭐⭐⭐
分布式锁 高并发、扣款、秒杀 性能好 需 Redis ⭐⭐⭐⭐
状态机 订单/流程状态流转 无侵入 业务限定 ⭐⭐⭐⭐
乐观锁 更新、扣库存 高并发 需版本字段 ⭐⭐⭐
悲观锁 低并发强一致 简单 性能差

四、企业级最佳实践(必看)

  1. 查询接口天然幂等,不用处理
  2. 插入优先用唯一索引
  3. 订单/支付/表单提交 必用 Token 机制
  4. 高并发扣减 用分布式锁 + 乐观锁
  5. 状态流转业务 用状态机控制
  6. 所有幂等控制必须是原子操作(Redis SET/DEL、数据库唯一索引/行锁)
  7. 重复请求不抛异常,直接返回成功(幂等语义)

五、伪代码示例(Token 方案,最常用)

java 复制代码
// 1. 获取 Token 接口
String getToken() {
    String token = UUID.randomUUID().toString();
    redis.set(token, token, 5, TimeUnit.MINUTES); // 5分钟过期
    return token;
}

// 2. 业务提交接口(幂等核心)
Result submit(String token, Order order) {
    // 原子删除:成功=第一次请求,失败=重复请求
    Boolean success = redis.delete(token);
    
    if (!success) {
        return Result.success("重复请求,已幂等处理");
    }

    // 正常执行业务
    orderService.save(order);
    return Result.success("提交成功");
}

总结

  1. 幂等性 = 一次请求和多次请求结果一致
  2. 分布式下最通用方案:Token 机制
  3. 最简单方案:数据库唯一索引
  4. 高并发方案:Redis 分布式锁
  5. 所有实现必须基于原子操作,才能保证绝对安全
相关推荐
装不满的克莱因瓶1 小时前
深入理解 Redisson 原理:Redis 不只是缓存,更是分布式协调器
java·redis·分布式·架构·分布式锁·redisson
Curvatureflight19 小时前
接口幂等性设计:如何避免重复提交、重复扣款和消息重复消费?
分布式·后端·架构
Kyrie_Li21 小时前
Kafka-基础知识总结
运维·分布式·kafka
江华森1 天前
XXL-JOB 分布式任务调度平台深度学习指南
分布式
m0_736034851 天前
ceph分布式存储
分布式·ceph
冷色调的咖啡师1 天前
1.大数据架构技术 上——搭建分布式Hadoop集群
大数据·linux·hadoop·分布式·hdfs·架构·yarn
坤昱2 天前
cfs调度类深入解刨——最新内核细节分析5
linux·分布式·cfs调度·eevdf调度·linux调度·linux技术·kernel最新版本内容
AI人工智能+电脑小能手2 天前
【大白话说Java面试题 第91题】【Mysql篇】第21题:分布式锁的使用场景和原理?
java·数据库·分布式·mysql·面试
JAVA社区2 天前
Java高级全套教程(十三)—— 分布式锁超详细实战详解(原理+三种方案企业级落地)
java·开发语言·分布式·spring cloud·面试·java-zookeeper