V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
loszhang
V2EX  ›  Java

批量支付 1000 笔以上运单

  •  
  •   loszhang · 250 天前 · 2166 次点击
    这是一个创建于 250 天前的主题,其中的信息可能已经有所发展或是发生改变。
    我们客户现在有个需求,需要支持一次请求可以发起 1000 笔以上运单的支付,主要集中在月底,大概有 6 万以上的运单。目前我们支持的最大数量是 200 笔,是同步请求,就是一次请求,会把运单的状态修改为支付处理中,修改钱包,记录运单状态的其他数据,大概每笔运单需要修改或者新增 5 张表,然后向 mq 写入一条支付消息,用于向支付平台发起支付的请求,这是一笔运单需要做的,最后会把这 200 笔运单结果返回给前端,实际的支付会由 mq 去处理。现在如果要处理 1000 笔以上的运单,肯定不能再用同步请求,主要就是把这 1000 笔运单记录下来,修改状态,不能让客户将这 1000 笔运单再发起一次支付请求。不知道大家有没有好的处理方案。
    33 条回复
    wolfan
        1
    wolfan  
       250 天前   ❤️ 1
    前端给个加载打印的,就一行一行打印成交信伪装成处理,后台分批提交 6W 单不就好了。
    loszhang
        2
    loszhang  
    OP
       250 天前
    @wolfan 我们现在的方案是,前端发起请求后,后端直接返回类似交易已受理的结果,前端不用再等待后端对这笔请求中 1000 笔运单的一个预处理(修改运单状态及相关表),因为这个太耗时,所以我们后端接收到这 1000 笔运单后,先记录下 1000 笔运单的 id ,异步去处理,但是在这处理这笔运单时,客户还会继续发起下一次 1000 笔运单的支付请求,这个 1000 笔运单中,肯定会有上次请求还未来得及修改运单状态的运单,这个就会造成重复支付的可能,所以我们首先需要将每次请求中的运单 id 记录下来,存储到 redis 或者数据库,这样在 MQ 发起支付时,判断下每笔运单的状态,如果是处理中,则直接跳过,不再处理。目前我主要犹豫不定的是把这 1000 笔运单的 id 存储到 redis 中还是数据库中。
    wuyiccc
        3
    wuyiccc  
       250 天前
    状态补充一个处理中的中间状态,提交的这 1000 单修改状态为处理中,等后续异步处理完成之后再修改为处理完成,并发问题给记录加一个 version 字段,并发更新的时候判断版本号是否相等
    yc8332
        4
    yc8332  
       250 天前
    其实你现在不就是异步支付吗?发起支付后应该不用再人工参与了吧?如果是这样直接批量更新成支付中,可以分布式锁再加个标志保险一点。接下来就和用户无关了,他也只能发起其他的未支付订单
    loszhang
        5
    loszhang  
    OP
       250 天前
    @wuyiccc 就是在用户发起这 1000 笔运单支付请求是,我要异步记录 1000 笔运单的处理中状态,还是同步记录,如果是异步记录,直接返回给用户处理中的结果,这样用户查到的可能还有没来及的记录处理中状态的运单。如果同步去记录这 1000 笔运单,需要的时间会长些,会让用户在界面有个等待的时间。
    loszhang
        6
    loszhang  
    OP
       250 天前
    @yc8332 对,发起后就不需要用户再参与了,等待最后的处理结果就行,失败或者成功都会记录到运单状态中。
    lsk569937453
        7
    lsk569937453  
       250 天前
    @loszhang 肯定是异步记录。用户发起 1000 笔运单支付请求后,前端直接展示"运单支付处理中,完成后将会发送短信通知,在此期间请不要重新提交新的运单支付。"就完事了。

    在用户的 1000 笔运单处理过程中,不允许用户再次提交另外的 1000 笔运单支付。这样是最简单的。
    None2
        8
    None2  
       250 天前
    「要异步记录 1000 笔运单的处理中状态,还是同步记录?」

    运单状态同步更改为 [处理中] ,异步做其他逻辑,异步处理完成后,状态再更改为 [已处理]
    yc8332
        9
    yc8332  
       250 天前
    @loszhang 那就是我发的那个流程就能走通了。对于有并发去做相同操作的记得加锁就行
    wuyiccc
        10
    wuyiccc  
       250 天前
    @loszhang 同步只修改 1000 单运单的支付状态为支付中,后面其他操作都改为异步操作,前面的这个只修改支付状态的处理逻辑也会很慢么?
    yc8332
        11
    yc8332  
       250 天前
    @loszhang 如果要给前端更好的体验,可以说支付完成了昨天异步拉取或者说推送结果
    iOCZS
        12
    iOCZS  
       250 天前
    那就前端轮询嘛,完成后刷新页面
    loszhang
        13
    loszhang  
    OP
       250 天前
    @lsk569937453 因为用户的运单量比较大,且支付时间集中,基本就是月底,大概 6 万以上,如果用户发起一次请求,直到运单全部处理完成之前不允许再支付,这个是不行的。
    loszhang
        14
    loszhang  
    OP
       250 天前
    @None2 如果是同步更改运单处理中的状态,我试下了,我是重新建了一张表,有个 id 主键字段,一个运单 id 字段,1000 条数据,我是循环插入,用了 40 秒。
    loszhang
        15
    loszhang  
    OP
       250 天前
    @yc8332 支付是使用异步处理。现在我犹豫不决的是发起支付请求如何去记录运单支付处理中的状态,因为有 1000 笔运单,我要同步去修改,耗时较长,如果把这部分也做成异步处理,用户刷新到运单数据,可能会有上次请求中还未修改状态的运单。
    loszhang
        16
    loszhang  
    OP
       250 天前
    @wuyiccc 我试下了循环插入 1000 条数据,40 秒。我再换下其他插入的方式。我们使用的是 mybatis.
    loszhang
        17
    loszhang  
    OP
       250 天前
    @iOCZS 我们可以先给前端一个处理中的结果,后续的支付都是异步,前端刷新就可以看到最新结果。
    perbugwei
        18
    perbugwei  
       250 天前
    mybatis 的 savebatch 那个方法?那个方法不能处理数据量大的情况,但是 1000 条数据也不会 40s ,是不是别的逻辑的问题。
    loszhang
        19
    loszhang  
    OP
       250 天前
    @perbugwei 不是 savebatch ,就是 insertSelective
    dong568789
        20
    dong568789  
       250 天前
    客户发起运单支付,这些运单不是应该先更新状态(除理中),后续是不是就查不到了,也不会有重复提交的问题
    loszhang
        21
    loszhang  
    OP
       250 天前
    @dong568789 如果是同步更新状态,就不会出现用户会重复提交的问题。如果是异步更新状态,就会出现。
    ftsland
        22
    ftsland  
       250 天前
    楼主的需求 redis 挺合适的啊, 根据客户 id 记录到 set 里, 你是怕 redis 的数据意外挂了没了吧, 同时把运单数据记录到日志里呗
    loszhang
        23
    loszhang  
    OP
       250 天前
    @perbugwei 我换成 foreach 插入,1000 笔,300 毫秒。那我就同步记录下请求中的运单 id ,查询时过滤掉已经记录的运单,这样就不会再重复发起。
    offswitch
        24
    offswitch  
       249 天前
    6w id 也不是很多,1000 笔订单,作为一次批处理,用户支付一次,记录这 1000 笔订单的 id ,甚至都不用逐个修改状态,如果重复提交已处理的订单 id ,后台直接抛出错误。


    前端设计上,考虑一下新开一个批量处理订单,这几万个订单一次性返回,一页展示 1000 行,这一页处理完成,前端过滤掉这一页,显示下一页。
    offswitch
        25
    offswitch  
       249 天前
    至于后面的修改订单状态,放到 mq 中一个个修改成处理中,之后订单一个个入队,走正常订单处理流程
    offswitch
        26
    offswitch  
       249 天前
    前端页面展示状态,后端你可以直接从 redis 中取订单的状态(批量提交的订单 id 一定要记录到数据库里面),如果有这个订单 id ,说明订单正在处理中,不用实际修改数据库中每一个订单的状态
    loszhang
        27
    loszhang  
    OP
       249 天前
    @offswitch 我主要是一开始不太确定,应该如何去更改这 1000 笔运单的状态,如果同步修改,可能会耗时比较久,给用户的感受不太好。我刚才换了中批量插入的方式,很快了。
    让前端设计下,这个一般前端是不太想参与的,😄。
    loszhang
        28
    loszhang  
    OP
       249 天前
    感谢大家的回复,我目前选择的方式是,用户发起批量支付时,同步将运单的 id 写入新表中,然后返给前端处理中的结果。前端查询时,我会过滤掉已经记录的运单,这样就不会造成用户重复发起。
    因为一开始我担心批量修改和插入会比较耗时,我试下了循环调用 insert 插入,确实比较费时,1000 条 40 秒,然后换成 foreach 插入,1000 条 300 毫秒。所以选择同步去记录运单。
    后续如果还有其他问题,也会更新,感谢。
    Satella
        29
    Satella  
       249 天前
    你们这个客户,应该是做网络货运的吧,说不定还带点违规那种
    lanfeng579
        30
    lanfeng579  
       249 天前
    @loszhang #28 你这个肯定不能循环 insert 啊 你循环插入 网络 IO 次数太多了,建议是接口进入的时候 你就去加分布式锁,不然的话 会有并发问题,撞上了同时点的快点,可能你还没存进去 他又发起了支付请求,这种 最好多测一下
    zhkn
        31
    zhkn  
       249 天前 via iPhone
    循环调用 insert1000 次也不应该是 40 秒吧,循环中还做了其他处理吗
    Jack66
        32
    Jack66  
       249 天前
    mq 处理没问题,注意数据一致性问题。好奇的是你们怎么自动支付,支付有这种功能吗,即使有的话,支付应该提供 api 批量支付,然后响应参数。
    yc8332
        33
    yc8332  
       249 天前
    @loszhang 加锁。之后你可以队列去更新数据库或者插入数据库。。尽量用批量更新/插入,时间少很多。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5780 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 01:41 · PVG 09:41 · LAX 17:41 · JFK 20:41
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.