• 如何避免并发情况下的重复提交


    如何避免并发情况下的重复提交

    背景

    在业务开发中,我们常会面对防止重复请求的问题。当服务端对于请求的响应涉及数据的修改,或状态的变更时,可能会造成极大的危害。重复请求的后果在交易系统、售后维权,以及支付系统中尤其严重。

    重复请求的一致性问题又称幂等性问题。



    先弄清楚啥叫幂等性。

    比如 
    1. 一个用户把他的性别设置为男,无论他设置多少次,他的性别都是男。 
    2. 比如我们查询余额(假设并没有任何使余额发生变化的行为),那么我们点击多少次查询余额得到的结果都应该是一样的。

    以上都是幂等的。

    但是,如果我们进行一笔交易,这笔交易实际已经正常的插入了数据库,但由于前台操作的抖动,快速操作,网络通信或者后端响应慢等原因,又来了一次。导致用户平白无故支付了两次。 这种情况我们该如何避免呢??


    首先前端优化是必不可少的,这里暂且不谈。。。

    我们谈谈后端

    唯一键法

    并发并不意味着每个request都处理的很快,也不意味着机器之间就不共享数据了。可以把所有带有副作用的task都给一个GUID,最后写进数据库之前查询一下这个GUID是否已经被执行过了。

    订单状态法

    用户调用支付,扣款成功后,更新对应订单状态,然后再保存流水。

    (支付状态:未支付,已支付)

    步骤: 
    1、查询订单支付状态 
    2、如果已经支付,直接返回结果 
    3、如果未支付,则支付扣款并且保存流水 
    4、返回支付结果

    理论上,只要在数据状态更新前完成了查询操作,则业务逻辑的重复处理就依旧会发生。

    基于缓存的数据验证

    Redis存储查询轻量快速。在request进来的时候,可以先记录在缓存中。后续进来的request每次进行验证。整个流程处理完成,清除缓存。

    I.  每次交易发起申请,读取缓存中是否有以orderId为key的值
    II. 没有,则往缓存中写入以orderId为key的value
    III.有,则说明有该订单正在进行。
    IV. 操作完清缓存,或者缓存存值的时候设置生命周期
    
    • 1
    • 2
    • 3
    • 4
    • 5

    利用数据库的主键唯一

    同一笔订单进来的话,数据库会报唯一索引的错误。这个时候后台对这个异常进行处理并返回给前端提示。

    (那么如何保证误操作的交易的订单号都是一个订单号呢,如何和正常的多次交易情况区分开?)

    缓存计数器

    由于数据库的操作比较消耗性能,了解到redis的计数器也是原子性操作。果断采用计数器。既可以提高性能,还不用存储,而且能提升qps的峰值。 
    还是以支付为例子:

    每次request进来则新建一个以orderId为key的计数器,然后+1。

    如果>1(不能获得锁): 说明有操作在进行,删除。 
    如果=1(获得锁): 可以操作。 
    操作结束(删除锁):删除这个计数器。

  • 相关阅读:
    centos7.x及centos8.x安装新版docker
    linux7/8版本的dockerce安装(2022/5/27亲测可行)
    heapq模块通过nlargest()和nsmallest()找到最大或最小的N个元素
    MPG线程模型简介
    协程与yield表达式
    亲测CentOS 8.2更换yum源报错Errors during downloading metadata for repository 'epel': Status code解决办法
    docker自定义bridge网络
    python自动换壁纸
    Python自动锁屏
    Python批量下载壁纸,保存壁纸,flask搭建壁纸网站
  • 原文地址:https://www.cnblogs.com/erma0-007/p/8654950.html
Copyright © 2020-2023  润新知