首页 > 其他分享 >防止重复下单

防止重复下单

时间:2024-08-21 10:26:57浏览次数:7  
标签:下单 重复 版本号 订单 防止 支付 主键 请求

一、如何避免重复下单

前端页面也可直接防止用户重复提交表单,但网络错误会导致重传,很多RPC框架、网关都有自动重试机制,所以重复请求在前端侧无法完全避免!问题最后还是如何保证服务接口的幂等性。

1.1 如何判断请求是重复的

  • 插入订单前,先查一下订单表,有无重复订单? 难以用SQL条件定义到底什么是“重复订单”
  • 订单的用户、商品、价格一样就是重复订单? 万一这用户就是连续下了俩一模一样订单呢?

所以保证幂等性要做到:

1.1.1 每个请求须有唯一标识

比如订单支付请求,得包含订单 id,一个订单 id 最多只能成功支付一次。

1.1.2 每次处理完请求后,须有记录标识该请求已被处理

在 MySQL 中记录一个状态字段。如支付之前记录一条这个订单的支付流水。

1.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,订单服务直接返回订单创建成功。

 

二、解决 ABA

2.1 什么是 ABA

如订单支付后,seller 要发货,发货完成后要填个快递单号。假设 seller 填个 666,刚填完,发现填错了,赶紧再修改成 888。对订单服务,这就是 2 个更新订单的请求。系统异常时 666 请求到了,单号更成 666,接着 888 请求到了,单号又更新成 888,但是 666 更新成功的响应丢了,调用方没收到成功响应,自动重试,再次发起 666 请求,单号又被更新成 666了,这数据显然就错了!

 

2.2 解决方案(乐观锁)

订单主表增加 version 列。每次查询订单时,版本号要随着订单数据返回给页面。页面在更新数据的请求中,把这个版本号作为更新请求的参数,带回给订单更新接口。

订单服务在更新数据的时候,需要比较订单的版本号是否和消息中的一致:

  • 不一致:拒绝更新数据
  • 一致:还需再更新数据的同时,将 version + 1。“比较版本号、更新数据和版本号 + 1”的过程须在同一事务执行

三、总结

  • 创建订单服务,可通过预生成订单号,然后利用 DB 的订单号唯一约束,避免重复写入订单,实现创建订单服务的幂等性
  • 更新订单服务,通过一个版本号机制,每次更新数据前校验版本号,更新数据同时自增版本号,这样的方式,来解决 ABA 问题,确保更新订单服务的幂等性

两种幂等的实现方法,就可以保证,无论请求是不是重复,订单表中的数据都是正确的。

实现订单幂等的方法,完全可以套用在其他需要实现幂等的服务中,只需要这个服务操作的数据保存在数据库中,并且有一张带有主键的数据表即可。

标签:下单,重复,版本号,订单,防止,支付,主键,请求
From: https://www.cnblogs.com/jiutang001/p/18371097

相关文章

  • 删除字符串中的所有相邻重复项(1047)
    题目描述给出由小写字母组成的字符串S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。在S上反复执行重复项删除操作,直到无法继续删除。在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。解题思路这里我们还是使用栈这个数据结构,我们还是遍历当前字符串,......
  • 【实战指南】使用 Redis 实现 Java 项目中的防重复提交功能:从原理到实战(实战篇)
    文章目录使用Redis实现Java项目中的防重复提交功能1.引言1.1为何需要防重复提交功能1.2重复提交导致的问题1.3引入Redis作为解决方案的一部分2.基础知识2.1Redis简介2.2Java环境搭建2.3环境配置3.理论基础3.1HTTP请求的特点3.2前端表单提交3.3后......
  • leetcode3.无重复字符的最长子串
    题目:给定一个字符串s,请你找出其中不含有重复字符的最长子串的长度。思路:滑动窗口算法。最大子串长度=右下标-左下标+1;使用map记录字符的下标,key为字符,value为下标。如果map的key出现重复,说明有重复字符了。找出左下标left,如果重复就刷新左下标。代码:publ......
  • 力扣面试经典算法150题:删除有序数组中的重复项 II
    删除有序数组中的重复项II今天的题目是力扣面试经典150题中的数组的中等难度题:删除有序数组中的重复项II题目链接:https://leetcode.cn/problems/remove-duplicates-from-sorted-array-ii/description/?envType=study-plan-v2&envId=top-interview-150题目描述给定一......
  • 代码随想录算法训练营day09|151.翻转字符串里的单词,卡码网:55.右旋转字符串,28.实现 str
    151.翻转字符串里的单词题目链接:https://leetcode.cn/problems/reverse-words-in-a-string/description/暴力removeExtraSpaces:voidremoveExtraSpaces(string&s){for(inti=s.size()-1;i>0;i--){if(s[i]==''&&s[i]=......
  • Linux 中 实现每一行连续重复
     001、sed实现[root@PC1test]#lsa.txt[root@PC1test]#cata.txt##测试数据010203040506070809101112131415161718192021222324252627282930[root@PC1test]#sed'1~1p'a.txt##每行连续重复两次......
  • 最长无重复子串
    无重复字符的最长子串这个问题两个思路,要么进行遍历暴力破解,要么进行滑动窗口(巧妙),下面先看一下暴力解法:classSolution:deflengthOfLongestSubstring(self,s:str)->int:s_count=len(s)max_list=[]ifs_count==0:re......
  • 3_无重复字符的最长子串
    3_无重复字符的最长子串【问题描述】给定一个字符串s,请你找出其中不含有重复字符最长子串的长度。示例:输入:s="abcabcbb"输出:3解释:因为无重复字符的最长子串是"abc",所以其长度为3。【算法设计思想】此题为典型的滑动窗口问题,这类问题的主要是处理数组或者字......
  • laravel 批量插入并在遇到重复键时更新
    /***批量插入并在遇到重复键时更新*@paramarray$values*@returnbool*/publicstaticfunctioninsertOnDuplicate(array$values){if(empty($values)){returntrue;}if(!is_array(reset($values))){$values=[$values];......
  • Amazing-Py-Scripts:用Python代码脚本实现一键自动化,告别重复性工作,提升工作效率
    你是否厌倦了枯燥的重复性工作?是否渴望用代码创造出有趣的工具来提升效率?那么,Amazing-Python-Scripts将会成为你的秘密武器!这个GitHub仓库汇集了大量实用且有趣的Python脚本,涵盖从基础到高级,从自动化任务到娱乐应用,旨在帮助你轻松实现自动化、提高工作效率、并用代码点缀......