广州企业网站建设方案阳澄湖大闸蟹网站建设
2026/1/16 14:11:43 网站建设 项目流程
广州企业网站建设方案,阳澄湖大闸蟹网站建设,网站建设赚钱吗,荣成做网站#x1f4a5; 前言#xff1a;那个让公司损失 50 万的“双击” 场景还原#xff1a; 双十一零点#xff0c;用户手抖#xff0c;对着“立即支付”按钮连点了两下。 由于网络抖动#xff0c;前端的防抖失效了#xff0c;两个 HTTP 请求几乎同时打到了后端。 结果#xf… 前言那个让公司损失 50 万的“双击”场景还原双十一零点用户手抖对着“立即支付”按钮连点了两下。由于网络抖动前端的防抖失效了两个 HTTP 请求几乎同时打到了后端。结果订单被支付了两次库存扣了两份用户发飙财务对账对到崩溃。这就是典型的幂等性丢失事故。在分布式系统中网络超时是常态。我们无法控制请求会重复发送但我们必须保证同一个请求无论执行多少次产生的效果是一样的。很多初级开发者的反应是“在数据库建个唯一索引去重表不就行了”Too Young.在高并发大流量下数据库的唯一索引会成为最大的性能瓶颈甚至引发死锁。今天我们就来拆解大厂都在用的**“Token 机制 状态机”**组合方案彻底终结重复提交️ 方案一Redis Token 机制 (防抖神器)这是目前互联网大厂最通用的方案主要用于解决**“用户重复提交”和“前端重试”**。核心原理申请令牌客户端在进入提交页面时先调用后端接口获取一个全局唯一的 Token后端将其存入 Redis。携带提交客户端发起业务请求时如“提交订单”必须在 Header 中带上这个 Token。核销令牌后端收到请求去 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 脚本Stringscriptif redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end;// Java 调用LongresultredisTemplate.execute(newDefaultRedisScript(script,Long.class),Collections.singletonList(tokenKey),tokenValue);if(result0){thrownewIdempotencyException(请勿重复提交);}// 继续执行业务...⚙️ 方案二状态机幂等 (业务层的最后防线)Token 机制能挡住 99% 的用户手抖但挡不住MQ 消息重试或者恶意攻击。对于订单支付这类核心业务我们必须利用业务本身的状态流转来实现天然幂等。核心原理订单状态流转是有序的待支付-支付中-已支付。我们只需要在 SQL 更新时带上**“前置状态”**作为条件乐观锁思想。SQL 实战-- 场景处理支付成功回调-- 错误写法直接更新UPDATEorderSETstatusPAIDWHEREorder_id123;-- 正确写法状态机控制 (CAS)UPDATEorderSETstatusPAID,pay_timeNOW()WHEREorder_id123ANDstatuspaying;-- 关键在这里分析第一次请求当前状态是paying匹配成功更新为PAID返回影响行数 1。第二次请求重复当前状态已经是PAID了条件status paying不成立返回影响行数 0。代码判断影响行数为 0直接返回“处理成功”幂等成功而不需要报错。Java 代码示例intupdateCountorderMapper.updateStatus(orderId,OrderStatus.PAID,OrderStatus.PAYING);if(updateCount0){// 没更新到说明已经支付过了或者是状态不对// 此时应查询最新状态如果是 PAID 则直接返回成功幂等OrderorderorderMapper.selectById(orderId);if(order.getStatus()OrderStatus.PAID){returnsuccess;// 幂等处理}else{thrownewBizException(订单状态异常);}} 方案三数据库唯一键 (去重表)虽然被大厂嫌弃性能低但在并发不高或者数据极其重要的场景如金融账务流水它依然是兜底的神。核心思路建立一张uniq_request表对biz_idsource建立唯一索引。业务执行前先往这张表 insert。Insert 成功 - 执行业务。Insert 失败 (Duplicate Key) - 说明重复直接返回。优点绝对可靠不依赖 Redis。缺点数据库写瓶颈分库分表后处理麻烦。 总结大厂的“组合拳”在真实的亿级系统中我们从不依赖单一方案而是采用漏斗式防御第一层前端按钮点击后 Disable防止帕金森式手抖。第二层网关/APIRedis Token 机制。拦截 99% 的重复请求减轻数据库压力。第三层Service/DAO状态机 (CAS)或唯一索引。作为最后一道防线保证数据绝对一致。没有最好的方案只有最适合场景的方案。博主留言你在生产环境遇到过“重复扣款”的事故吗在评论区回复“幂等”我发给你一份《Redis Lua 脚本通用工具类 状态机引擎源码》直接复制到项目里就能用

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询