后端怎样防止重复提交订单?

article/2025/8/19 18:41:56
 

点击上方关注 “终端研发部

设为“星标”,和你一起掌握更多数据库知识

一般我们都是这样做的:

创建订单的时候,用订单信息计算一个哈希值,判断redis中是否有key,有则不允许重复提交,没有则生成一个新key,放到redis中设置个过期时间,然后创建订单。其实就是在一段时间内不可重复相同的操作

5831bd510f0f0dd30b3855a67acae15c.jpeg

第二种方式:利用唯一索引机制的验证

需要原子性操作,想到了数据库的唯一索引。新建一个TradeLock表:

CREATE TABLE `TradeLock` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`type` int(11) NOT NULL COMMENT '锁类型',
`lockId` int(11) NOT NULL DEFAULT '0' COMMENT '业务ID',
`status` int(11) NOT NULL DEFAULT '0' COMMENT '锁状态',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='Trade锁机制';

● 每次request进来则往表里面插入数据:

成功,则可以继续操作(相当于获取锁);

失败,则说明有操作在进行。

● 操作完成后,删除此条记录。(相当于释放锁)。

第二种方法(利用数据库完整性约束)最简便,但是会访问(读写)数据库,给数据库造成一定的压力;
同时也有个隐患,程序执行中途故障了(网络垮了,服务宕了...),后面重复提交,就无法成功了

也有解决方法:定时器清理这个hash数据库表

第三种方式:利用redis的setNX

第四种方式:利用redis的分布式锁(同上setNx命令

  1. set命令

  2. Redission框架)

如果是防重设计,流程图要改改:

208701126683615ed94d92272b48522a.png

利用AOP+Redis实现案例

1.自定义注解

/*** @author Tzeao*/
@Target(ElementType.METHOD) // 作用到方法上
@Retention(RetentionPolicy.RUNTIME) // 运行时有效
public @interface NoRepeatSubmit {//名称,如果不给就是要默认的String name() default "name";
}

2.使用AOP实现该注解

/*** @author Tzeao*/
@Aspect
@Component
@Slf4j
public class NoRepeatSubmitAop {@Autowiredprivate RedisService redisService;/*** 切入点*/@Pointcut("@annotation(com.qwt.part_time_admin_api.common.validation.NoRepeatSubmit)")public void pt() {}@Around("pt()")public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();assert attributes != null;HttpServletRequest request = attributes.getRequest();//这里是唯一标识 根据情况而定String key = "1" + "-" + request.getServletPath();// 如果缓存中有这个url视为重复提交if (!redisService.haskey(key)) {//通过,执行下一步Object o = joinPoint.proceed();//然后存入redis 并且设置15s倒计时redisService.setCacheObject(key, 0, 15, TimeUnit.SECONDS);//返回结果return o;} else {return Result.fail(400, "请勿重复提交或者操作过于频繁!");}}
}

3、测试

@NoRepeatSubmit(name = "test") // 也可以不给名字,这样就会走默认名字@GetMapping("test")public Result test() {return Result.success("测试阶段!");}

悲观锁和乐观锁

具体步骤:先根据id查询用户信息,包含version字段根据id和version字段值作为where条件的参数,更新用户信息,同时version+1判断操作影响行数,如果影响1行,则说明是一次请求,可以做其他数据操作。如果影响0行,说明是重复请求,则直接返回成功。

在更新数据之前先查询一下数据:

select id,amount,version from user id=123;

如果数据存在,假设查到的version等于1,再使用idversion字段作为查询条件更新数据:

update user set amount=amount+100,version=version+1
where id=123 and version=1;

更新数据的同时version+1,然后判断本次update操作的影响行数,如果大于0,则说明本次更新成功,如果等于0,则说明本次更新没有让数据变更。

由于第一次请求version等于1是可以成功的,操作成功后version变成2了。这时如果并发的请求过来,再执行相同的sql:

update user set amount=amount+100,version=version+1
where id=123 and version=1;

update操作不会真正更新数据,最终sql的执行结果影响行数是0,因为version已经变成2了,where中的version=1肯定无法满足条件。但为了保证接口幂等性,接口可以直接返回成功,因为version值已经修改了,那么前面必定已经成功过一次,后面都是重复的请求。

具体流程如下:

2ef03964a148b982458b3ffb12900c9e.jpeg

对于创建订单和更新订单

创建订单服务,可通过预生成订单号,然后利用DB的订单号唯一约束,避免重复写入订单,实现创建订单服务的幂等性

更新订单服务,通过一个版本号机制,每次更新数据前校验版本号,更新数据同时自增版本号,这样的方式,来解决ABA问题,确保更新订单服务的幂等性

总结

对于重复提交请求的问题,我们单纯的只从前端或后端控制,带来的用户体验都不是最好的。只有两者结合起来,才能在确保功能正常的前提下,保证用户体验效果。

PS:如果想学习技术,或者在学习技术的过程中有疑问,对编程方向的选择,可以来这里找小于哥,一个有思想有规划,被代码延误的心灵导师,可咨询offer的选择,职业规划,学习路线,技术开发中的问题

我是终端研发部的小于哥

@终端研发部

天专注技术开发小技巧,技术教程进阶,职场经验,面试的分享,希望我的回答能够帮助到你哈,笔芯~

cdecd104bb18f3137105ace4e743d3e1.jpeg

回复 【idea激活】即可获得idea的激活方式

回复 【Java】获取java相关的视频教程和资料

回复 【SpringCloud】获取SpringCloud相关多的学习资料

回复 【python】获取全套0基础Python知识手册

回复 【2020】获取2020java相关面试题教程

回复 【加群】即可加入终端研发部相关的技术交流群

用 Spring 的 BeanUtils 前,建议你先了解这几个坑!

lazy-mock ,一个生成后端模拟数据的懒人工具

在华为鸿蒙 OS 上尝鲜,我的第一个“hello world”,起飞!

字节跳动一面:i++ 是线程安全的吗?

一条 SQL 引发的事故,同事直接被开除!!

太扎心!排查阿里云 ECS 的 CPU 居然达100%

一款vue编写的功能强大的swagger-ui,有点秀(附开源地址)

相信自己,没有做不到的,只有想不到的

在这里获得的不仅仅是技术!

0535b33f218abf45383e8346c304bdc5.png

36cfe462992ed1cde3a2e350f54794d3.gif

喜欢就给个“在看e4d68cd0a20544a6fb41ed499320b52f.gif


http://chatgpt.dhexx.cn/article/m1RwuYXa.shtml

相关文章

后端怎么防止重复提交订单

前言 接口幂等性问题,对于开发人员来说,是一个跟语言无关的公共问题。本文分享了一些解决这类问题非常实用的办法,绝大部分内容我在项目中实践过的,给有需要的小伙伴一个参考。 不知道你有没有遇到过这些场景: 有时…

bootstrap订单提交页面

下载地址 基于bootstrap实现的订单提交页面,常见的电商购物网站订单确认提交页面。 dd:

移动端-确认订单页面

项目准备 lib文件中存放外来的文件,就比如这个项目使用到字体图标,那存放的就是字体图标的文件,css 样式,images 重要的图片,uploads 页面随时更新的图片,其次就是html文件。 base.css *{margin: 0;pad…

confirm-order提交订单

目录 顶部导航条:复用head组件新增收货地址订单收货地址页面顶部导航条:复用head组件无地址地址列表新增地址 增加收货地址add_address顶部导航条:复用head组件地址信息表单其他组件 送达时间商店商品底部弹出消息:复用alertTip组…

订单。。。

一、库存扣减和订单表不一致 1、网络抖动—网速是好是坏,不稳定。最大延迟与最小延迟的时间差,如最大延迟是20毫秒,最小延迟为5毫秒,那么网络抖动就是15毫秒 2、库存数据不一致的原因: 1)事务性的问题 – …

实现提交订单的功能

根据购物车中的商品名称和数量生成了结算信息&#xff0c;并可以填写收货人姓名、联系电话和收货地址&#xff0c;本任务将实现提交订单的功能。 一、创建订单页面order.jsp <% page language"java" import"java.util.*" pageEncoding"UTF-8"…

美多商城项目:结算订单与提交订单

一、结算订单 1. 结算订单逻辑分析 结算订单是从Redis购物车中查询出被勾选的商品信息进行结算并展示。 2. 结算订单接口设计和定义 1.请求方式 选项方案请求方法GET请求地址/orders/settlement/ 3. 结算订单后端逻辑实现 class OrderSettlementView(LoginRequiredMixin, Vie…

电商系统-提交订单并发处理

在多个用户同时发起对一个商品的下单请求时&#xff0c;先查询商品库存&#xff0c;再修改商品库存&#xff0c;会出现资源竞争问题&#xff0c;导致库存的最终结果出现异常。 1、并发下单问题演示 每个不同的用户在程序上&#xff0c;我们可以理解成不同的线程&#xff0c;每…

Android Studio_Toast消息提醒

Android Studio_Toast消息提醒 1、Toast是Android系统提供的一种非常简洁的消息提醒方式&#xff0c;程序中可以使用它实现将短小的消息通知给用户&#xff0c;一点时间后自动消失&#xff0c;且不占用屏幕的任何空间。 2、Toast用法其实非常简单&#xff0c;通过静态方法make…

Vue 消息提示通知的几种方式汇总

Vue 消息提示通知组件&#xff08;Message /Notification&#xff09;是我们日常开发中经常使用的组件&#xff0c;它可用作与用户交互的反馈提示&#xff0c;信息提交成功、错误、操作警告等场景使用。原生JavaScript 提供了 alert、prompt、confirm 等方法 提示框1>Messag…

java信息提醒怎么实现_jsp怎么实现消息提醒

如果你是平台级别的系统,可以考虑消息队列的中间件,例如:阿里巴巴的rocketmq,用这个来做消息订阅与分发。 如果你只是简简单单的需要提示到web(jsp)页面,可以用js定时ajax访问后台,后台来确定是否有数据更新,无论这个数据是哪来的。 推荐课程:Java教程。 这里就使用在JSP页面…

RabbitMQ真延时队列实现消息提醒功能

RabbitMQ真延时队列实现消息提醒功能 一、需求场景 用户可以制定多个计划&#xff0c;同时可给该计划设置是否需要到点提醒&#xff0c;且中途可以取消提醒或修改提醒时间。 二、需要解决的问题 学习过rabbitmq的同学们都知道&#xff0c;通过TTL死信队列可以实现延时队列的…

企业微信 消息 html,企业微信怎么设置消息提醒

企业微信是一款非常不错的办公软件&#xff0c;用户加入企业群就能实时了解企业的动态。而且大家只需设置消息提醒&#xff0c;软件就会在第一时间通知你&#xff0c;不会让你错过任何重要的消息&#xff0c;下面小编为大家带来相关的设置教程。 方法/步骤分享&#xff1a; 1、…

vue websocket 新消息提醒

概述&#xff1a; 不是当前聊天&#xff0c;有其他消息来就通过2种方式接受到提醒。在连接的上下文中判断&#xff0c;符合条件的弹框&#xff0c;显示红点&#xff0c;此处调用了element弹框组件列表点击事件&#xff0c;红点消失列表显示&#xff0c;属性中包含小红点 前提…

html5载入提示音,html5新消息提示声音

【实例简介】 【实例截图】 【核心代码】HTML5手机声音提示 #chatBox{width:400px;border:1px solid #d3d3d3;margin:50px auto;} #chat {max-height:220px;overflow-y:auto;max-width:400px;} #chat > ul > li{padding:3px;clear:both;padding:4px;margin:10px 0px 5px …

【Android】消息提示notification

notification 1、notification消息提示 由Android系统来管理和维护的&#xff0c;因此用户可以随时进入查看。某些信息不需要用户马上处理&#xff0c;可以利用通知&#xff0c;即延迟消息&#xff0c;比如软件的更新、短信、新闻等。 2、消息包含的内容 3、代码 <Button…

消息提醒系统:设计模式与实现方案 (公告(通告)、消息、提醒等基本功能数据库表设计与实现)

参考地址&#xff1a; 公告(通告),消息,提醒等基本功能数据库表设计_DamonREN的博客-CSDN博客 多种消息提醒系统的设计模式、实现方案&#xff08;附功能截图表结构&#xff09;_黑夜的风的博客-CSDN博客_消息提醒 设计一个百万级的消息推送系统 - crossoverJie - 博客园 案…

android开发 app消息提醒功能,APP消息提醒设计:ios和android的最佳设计方案 – 25学堂...

我们都知道APP一项重要功能就是消息推送,那么通知栏的设计极大程度上反应了这个APP是否合理,那如何可以方便地为用户展示各种通知内容。也就将是我们APP设计师跟APP产品经理重点思考的问题?也要关注移动APP布局设计经验之道! 自从去年发布的iOS5中也引入了这一功能,以替代…

vue浏览器消息提示

vue浏览器消息提示 JS部分 //判断浏览器是否支持浏览器消息弹窗 suportNotify() {if (window.Notification) {// 支持console.log("支持" "Web Notifications API");//如果支持Web Notifications API&#xff0c;再判断浏览器是否支持弹出实例this.show…

html如何设置提示收到消息,从零开始实现一个消息提示框

引言 消息提示框在实际应用场景当中比较常见,最常用的就是element ui的消息提示框,我们通常都是直接使用它们,但是我们有没有尝试过去探究其实现原理,并自己动手实现呢?为了提升我们的个人能力和竞争力,我们可以尝试来实现这样一个消息提示框。 实现效果 我们来查看一下最…