大厂如何解决订单幂等问题(50)

简介: 在分布式系统中,网络重试易导致重复请求,需保证接口幂等性。创建订单时可通过预生成唯一订单号,利用数据库主键唯一约束防止重复插入;更新订单时引入版本号机制,更新前校验并自增版本号,避免ABA问题。结合MySQL唯一索引与Redis标识,可有效实现各类服务的幂等处理,确保数据一致性。

一、问题背景最简单的:DB 事务。如创建订单时,同时往订单表、订单商品表插数据,这些 Insert 须在同一事务执行。Order 服务调用 Pay 服务,刚好网络超时,然后 Order 服务开始重试机制,于是 Pay 服务对同一支付请求,就接收到了两次,而且因为轮询负载均衡算法,落在了不同业务节点!所以一个分布式系统接口,须保证幂等性。二、如何避免重复下单前端页面也可直接防止用户重复提交表单,但网络错误会导致重传,很多RPC框架、网关都有自动重试机制,所以重复请求在前端侧无法完全避免!问题最后还是如何保证服务接口的幂等性。2.1 如何判断请求是重复的插入订单前,先查一下订单表,有无重复订单? 难以用SQL条件定义到底什么是“重复订单”订单的用户、商品、价格一样就是重复订单? 万一这用户就是连续下了俩一模一样订单呢?所以保证幂等性要做到:2.1.1 每个请求须有唯一标识比如订单支付请求,得包含订单 id,一个订单 id 最多只能成功支付一次。2.1.2 每次处理完请求后,须有记录标识该请求已被处理在 MySQL 中记录一个状态字段。如支付之前记录一条这个订单的支付流水。2.1.3 每次接收请求时,判断之前是否处理过若有一个订单已支付,就肯定已有一条支付流水。若重复发送这个请求,则此时先插入/支付流水,发现 orderId 已存在,唯一约束生效,报错重复 Key。就不会再重复扣款。在往 DB 插记录时,一般不提供主键,而由 DB 在插入时自动生成。这样重复的请求就会导致插入重复的数据。MySQL 的主键自带唯一性约束,若在一条 INSERT 语句提供主键,且该主键值在表中已存在,则该条 INSERT 会执行失败。因此可利用 DB 的“主键唯一约束”,在插数据时带上主键,以此实现创建订单接口的幂等性。给 Order 服务添加一个“orderId 生成”的接口,无参,返回值就是一个【全局唯一】订单号。在用户进入创建订单页面时,前端页面先调用该 orderId 生成接口得到一个订单号,在用户提交订单时,在创建订单的请求中携带该订单号。该订单号其实就是订单表的主键,于是,重复请求中带的都是同一订单号。订单服务在订单表中插入数据的时候,执行的这些重复 INSERT 语句中的主键,也都是同一个订单号。而 DB 唯一约束保证,只有一次 INSERT 执行成功。实际要结合业务,如使用 Redis,用 orderId 作为唯一K。只有成功插入这个支付流水,才可执行扣款。要求是支付一个订单,须插入一条支付流水,order_id 建立一个唯一键。你在支付一个订单前,先插入一条支付流水,order_id 就已经传过去了。就能写一个标识到 Redis 中,set order_id payed,当重复请求过来时,先查 Redis 的 order_id 对应的 value,若为 payed 说明已支付,就别再重复支付然后再重复支付订单时,写尝试插入一条支付流水,DB 会报唯一键冲突,整个事务回滚。保存一个是否处理过的标识也可以,服务的不同实例可以一起操作 Redis。 若因重复订单导致插入 t_order 失败,则 Order 服务不要把该错误返给前端页面。否则,就可能出现用户点击创建订单按钮后,页面提示创建订单失败,而实际上订单创建成功了。正确做法:这种 case,订单服务直接返回订单创建成功。三、解决 ABA3.1 什么是 ABA如订单支付后,seller 要发货,发货完成后要填个快递单号。假设 seller 填个 666,刚填完,发现填错了,赶紧再修改成 888。对订单服务,这就是 2 个更新订单的请求。系统异常时 666 请求到了,单号更成 666,接着 888 请求到了,单号又更新成 888,但是 666 更新成功的响应丢了,调用方没收到成功响应,自动重试,再次发起 666 请求,单号又被更新成 666了,这数据显然就错了!3.2 解决方案订单主表增加 version 列。每次查询订单时,版本号要随着订单数据返回给页面。页面在更新数据的请求中,把这个版本号作为更新请求的参数,带回给订单更新接口。订单服务在更新数据的时候,需要比较订单的版本号是否和消息中的一致:不一致:拒绝更新数据一致:还需再更新数据的同时,将 version + 1。“比较版本号、更新数据和版本号 + 1”的过程须在同一事务执行在这条 SQL 的 WHERE 条件中,version 值需要页面在更新的时候通过请求传进来。通过该版本号,就能保证,从我打开这条订单记录开始,一直到我更新这条订单记录成功,期间没有其他人修改过该订单数据。若有,则 DB 中的 version 就会改变,那我的更新操作就会执行失败。我就只能重新查询新版本的订单数据,再尝试更新。有了这个版本号,前文的 ABA 即有两个 case:把运单号更新为 666 成功,更新为 888 的请求带着旧版本号,就更新失败,页面提示用户更新 888 失败666 更新成功后,888 带着新版本号,888 更新成功。这时即使重试的 666 请求再来,因为它和上一条 666 请求带相同版本号,上一条请求更新成功后,这个版本号已经变了,所以重试请求的更新必然失败无论哪种情况,DB 中的数据与页面上给用户的反馈都是一致的。这就实现了幂等更新且避免 ABA。 四、总结两种幂等的实现方法,就可以保证,无论请求是不是重复,订单表中的数据都是正确的。创建订单服务,可通过预生成订单号,然后利用 DB 的订单号唯一约束,避免重复写入订单,实现创建订单服务的幂等性更新订单服务,通过一个版本号机制,每次更新数据前校验版本号,更新数据同时自增版本号,这样的方式,来解决 ABA 问题,确保更新订单服务的幂等性实现订单幂等的方法,完全可以套用在其他需要实现幂等的服务中,只需要这个服务操作的数据保存在数据库中,并且有一张带有主键的数据表即

相关文章
|
9月前
|
NoSQL 数据库 Redis
《微服务幂等性踩坑实录:从资损到全链路零故障的7个关键突破》
本文记录了团队因微服务接口缺乏幂等设计,在电商大促中因重复支付回调导致资损后,重构全链路幂等方案的实战经历。团队曾陷入三大误区:迷信“唯一ID+数据库唯一索引”,却因分布式ID重复、数据库锁阻塞在高并发下失效;忽略业务状态流转,导致重复请求触发库存超卖;过度依赖粗粒度分布式锁,因锁过期、误释放引发订单阻塞。最终通过“精准锁Key+锁续期+归属校验”“业务状态白名单+数据库行锁”等方案解决问题,核心结论为:幂等设计不是依赖单一工具,而是技术方案与业务逻辑的深度融合。
451 9
|
缓存 监控 测试技术
Python 3.12 目标:还可以更快!
Python 3.12 目标:还可以更快!
378 0
|
7月前
|
人工智能 NoSQL 前端开发
面试真题
多套AI与Java面试题涵盖RAG、智能体、大模型部署、分布式系统、JVM调优、数据库设计等核心技术,深入考察项目经验、架构能力与技术深度,适用于中高级工程师岗位选拔。
|
4月前
|
人工智能 机器人 数据挖掘
OpenClaw(Clawdbot)阿里云/本地部署及避坑指南+153个科研 Skill :AI Agent 变身专属实验室
当一位科研人员被RNA-seq数据分析、化合物结构预测、临床报告撰写等重复劳动占据大量时间时,OpenClaw的153个科学计算Skill全家桶给出了颠覆性解决方案——这些Skill并非简单插件,而是将生物信息、化学药物、临床医学、数据科学等领域的专业知识与工具封装成的“科研超能力”。只需一句自然语言指令,AI就能调用对应Skill,完成从数据质控、分析建模到论文写作、图表生成的全流程操作,让科研人员从机械劳动中解放,专注核心创意与科学发现。
3485 7
|
3月前
|
JavaScript 前端开发 测试技术
前端开发环境搭建:Node.js、npm与VSCode指南
在当今快速发展的前端开发领域,一个高效、稳定的开发环境是提升生产力的关键。Node.js、npm和VSCode作为现代前端开发的三大核心工具,能够帮助开发者轻松管理依赖、运行脚本以及编写高质量代码。本文将介绍如何搭建这一开发环境,并深入探讨几个关键方面,助你快速上手。
|
28天前
|
人工智能 监控 安全
大模型应用:医疗AI智能体幻觉防控:幻觉抑制的四层防护体系应用实践.128
医疗AI幻觉指大模型生成虚假、无据、矛盾的医疗信息,危及生命安全。本文提出“事前约束—事中校验—人工兜底—反馈闭环”四层防护体系,通过提示词强制规范、规则引擎实时拦截、医师审核白名单、用户反馈持续迭代,将幻觉率压至<0.5%,实现医疗场景零容忍安全落地。
195 1
|
7月前
|
SQL 关系型数据库 MySQL
MySQL SQL 优化
MySQL性能优化涵盖索引设计、查询语句改进、表结构调整与配置调优。合理使用索引、避免SELECT *、善用EXPLAIN分析执行计划,结合慢查询日志与Performance Schema监控,提升数据库响应速度,降低资源消耗,保障应用高效稳定运行。(238字)
|
3月前
|
存储 人工智能 运维
Tair 短期记忆架构实践:淘宝闪购 AI Agent 的秒级响应记忆系统
本文介绍淘宝闪购与千问合作的“一句话点外卖”项目中,Tair如何作为AI Agent短期记忆层核心:通过List/Hash混合数据模型、会话级分布式锁、多线程内核与弹性扩缩容,实现毫秒级低延迟、高并发、强一致的记忆管理,支撑30秒极速下单。
|
7月前
|
存储 人工智能 Java
面试回答示例篇(重点)
结合黑马《天机AI》项目,详解SpringAI、LangChain4J、RAG、Tool Calling、MCP等核心技术。涵盖智能体设计、大模型选型、私有化部署、流式输出、上下文管理及大模型幻觉解决方案,助力Java开发者快速掌握企业级AI应用开发要点。(238字)
715 0