news 2026/4/16 16:46:54

接口幂等性 (Idempotency) 终极方案:除了“去重表”,大厂都在用的 Token 机制与状态机实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
接口幂等性 (Idempotency) 终极方案:除了“去重表”,大厂都在用的 Token 机制与状态机实战

💥 前言:那个让公司损失 50 万的“双击”

场景还原:
双十一零点,用户手抖,对着“立即支付”按钮连点了两下。
由于网络抖动,前端的防抖失效了,两个 HTTP 请求几乎同时打到了后端。
结果:订单被支付了两次,库存扣了两份,用户发飙,财务对账对到崩溃。

这就是典型的幂等性丢失事故。
在分布式系统中,网络超时是常态。我们无法控制请求会重复发送,但我们必须保证:同一个请求,无论执行多少次,产生的效果是一样的。

很多初级开发者的反应是:“在数据库建个唯一索引(去重表)不就行了?”
Too Young.在高并发大流量下,数据库的唯一索引会成为最大的性能瓶颈,甚至引发死锁。

今天,我们就来拆解大厂都在用的**“Token 机制 + 状态机”**组合方案,彻底终结重复提交!


🛡️ 方案一:Redis Token 机制 (防抖神器)

这是目前互联网大厂最通用的方案,主要用于解决**“用户重复提交”“前端重试”**。

核心原理:

  1. 申请令牌:客户端在进入提交页面时,先调用后端接口获取一个全局唯一的 Token,后端将其存入 Redis。
  2. 携带提交:客户端发起业务请求时(如“提交订单”),必须在 Header 中带上这个 Token。
  3. 核销令牌:后端收到请求,去 Redis 检查 Token 是否存在。
    • 如果存在 ->删除 Token-> 执行业务。
    • 如果不存在 -> 报错“请勿重复提交”。

流程图解:

用户客户端业务服务Redis缓存第一阶段:获取 Token1. 请求获取 Token2. SET IdempotencyToken UUID EX 3003. 返回 Token第二阶段:提交业务4. 提交订单 header带Token5. 检查并删除 Token LUA脚本返回 1 成功6. 执行业务逻辑7. 返回成功返回 0 失败8. 报错:重复请求alt[Token 校验成功][Token 不存在或已删除]用户客户端业务服务Redis缓存

⚠️ 致命坑点:先 Check 再 Delete?

很多新手会写出这样的代码:

// 错误示范!!!if(redis.hasKey(token)){redis.delete(token);// 假如这行代码执行前,并发请求刚好进来了怎么办?executeBusiness();}

在高并发下,这有“竞态条件” (Race Condition)!两个线程可能同时判断hasKey为真,然后都去执行业务。

正确姿势:原子性操作
必须使用Lua 脚本保证“检查+删除”是原子性的。

// Redis Lua 脚本Stringscript="if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";// Java 调用Longresult=redisTemplate.execute(newDefaultRedisScript<>(script,Long.class),Collections.singletonList(tokenKey),tokenValue);if(result==0){thrownewIdempotencyException("请勿重复提交");}// 继续执行业务...

⚙️ 方案二:状态机幂等 (业务层的最后防线)

Token 机制能挡住 99% 的用户手抖,但挡不住MQ 消息重试或者恶意攻击
对于订单支付这类核心业务,我们必须利用业务本身的状态流转来实现天然幂等。

核心原理:
订单状态流转是有序的:待支付->支付中->已支付
我们只需要在 SQL 更新时,带上**“前置状态”**作为条件(乐观锁思想)。

SQL 实战:

-- 场景:处理支付成功回调-- 错误写法:直接更新UPDATEorderSETstatus='PAID'WHEREorder_id=123;-- 正确写法:状态机控制 (CAS)UPDATEorderSETstatus='PAID',pay_time=NOW()WHEREorder_id=123ANDstatus='paying';-- 关键在这里!

分析:

  1. 第一次请求:当前状态是paying,匹配成功,更新为PAID,返回影响行数 1。
  2. 第二次请求(重复):当前状态已经是PAID了,条件status = 'paying'不成立,返回影响行数 0。
  3. 代码判断影响行数为 0,直接返回“处理成功”(幂等成功),而不需要报错。

Java 代码示例:

intupdateCount=orderMapper.updateStatus(orderId,OrderStatus.PAID,OrderStatus.PAYING);if(updateCount==0){// 没更新到,说明已经支付过了,或者是状态不对// 此时应查询最新状态,如果是 PAID 则直接返回成功(幂等)Orderorder=orderMapper.selectById(orderId);if(order.getStatus()==OrderStatus.PAID){return"success";// 幂等处理}else{thrownewBizException("订单状态异常");}}

🔒 方案三:数据库唯一键 (去重表)

虽然被大厂嫌弃性能低,但在并发不高或者数据极其重要的场景(如金融账务流水),它依然是兜底的神。

核心思路:
建立一张uniq_request表,对biz_id+source建立唯一索引
业务执行前,先往这张表 insert。

  • Insert 成功 -> 执行业务。
  • Insert 失败 (Duplicate Key) -> 说明重复,直接返回。

优点:绝对可靠,不依赖 Redis。
缺点:数据库写瓶颈,分库分表后处理麻烦。


📝 总结:大厂的“组合拳”

在真实的亿级系统中,我们从不依赖单一方案,而是采用漏斗式防御

  1. 第一层(前端):按钮点击后 Disable,防止帕金森式手抖。
  2. 第二层(网关/API)Redis Token 机制。拦截 99% 的重复请求,减轻数据库压力。
  3. 第三层(Service/DAO)状态机 (CAS)唯一索引。作为最后一道防线,保证数据绝对一致。

没有最好的方案,只有最适合场景的方案。


博主留言:
你在生产环境遇到过“重复扣款”的事故吗?
在评论区回复“幂等”,我发给你一份《Redis Lua 脚本通用工具类 + 状态机引擎源码》,直接复制到项目里就能用!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 9:03:39

基于Spring Boot的教学资料管理系统设计与实现

背景及意义在教育信息化持续推进的当下&#xff0c;高校教学资料种类与数量急剧增长&#xff0c;传统的人工管理方式愈发难以满足需求。教师和学生常常因资料查找困难、版本混乱等问题&#xff0c;耗费大量时间与精力。同时&#xff0c;不同院系、专业的教学资料缺乏统一、高效…

作者头像 李华
网站建设 2026/4/16 11:45:39

基于单片机的井盖安全监测与报警上位机监测系统设计

基于单片机的井盖安全监测与报警上位机监测系统设计概述 点击下载设计资料&#xff1a;https://download.csdn.net/download/m0_51061483/92081463 1.1 研究背景与设计意义 城市基础设施中&#xff0c;井盖广泛分布于道路、人行道、居民区和工业园区&#xff0c;主要用于覆盖…

作者头像 李华
网站建设 2026/4/16 10:45:29

强化学习Sarsa求最优策略

理论基础&#xff1a;注意&#xff1a;1. 超参数samples的设置&#xff1a;size of q_table grid_size*grid_size*action_size&#xff0c;每个 Q(s,a) 至少要访问 t 20&#xff5e;50 次&#xff0c;才能开始收敛&#xff0c;那么需要的总更新次数至少是(q_table)*t&#xff…

作者头像 李华
网站建设 2026/4/16 10:39:56

用姓氏改个巨有趣的家庭群名

#按姓氏起家庭群名&#xff01;笑到邻居来蹭群&#x1f923;李&#xff1a;李府茶话会&#x1f375;&#xff08;全家围坐唠嗑&#xff0c;比追剧还上头&#xff09;张&#xff1a;张灯结彩阁&#x1f3ee;&#xff08;天天像过节&#xff0c;热闹到停不下来&#xff09;王&…

作者头像 李华
网站建设 2026/4/16 9:01:16

探索高压无桥PFC:原理图、PCB与源代码之旅

高压无桥PFC原理图PCB源代码资料在电力电子领域&#xff0c;功率因数校正&#xff08;PFC&#xff09;技术对于提高电能利用效率、降低谐波污染至关重要。而高压无桥PFC作为一种新兴的技术方案&#xff0c;正逐渐崭露头角。今天&#xff0c;咱就一起来深入探究下高压无桥PFC的原…

作者头像 李华
网站建设 2026/4/16 10:40:05

探索 S7 - 1200 地铁屏蔽门控制系统

地铁屏蔽门控制系统&#xff0c;S7-1200地铁屏蔽门控制系统&#xff0c;地铁门控制系统&#xff0c;系统才用西门子博途&#xff0c;S7-1200PLC编程&#xff0c;wincc组态&#xff0c;包括PLC组态仿真画面在现代城市轨道交通中&#xff0c;地铁屏蔽门控制系统起着至关重要的作用…

作者头像 李华