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

如何避免“扣减了积分,但是没有成功创建订单”? - 分布式事务相关

  •  
  •   JasonLaw · 2022-01-15 21:51:44 +08:00 · 4358 次点击
    这是一个创建于 1048 天前的主题,其中的信息可能已经有所发展或是发生改变。

    假设现在有商城和会员两个服务,用户在商城使用积分购买商品,但是用户的积分是存储在会员服务的。

    现在的流程如下:

    扣减积分发生在会员服务中,创建订单及修改订单状态为已支付发生在商城服务中,所以会出现“扣减了积分,但是没有成功创建订单”这种情况。

    应该怎么解决这个问题呢?

    23 条回复    2022-01-19 02:26:15 +08:00
    nonoyang
        1
    nonoyang  
       2022-01-15 21:55:57 +08:00 via iPad
    这应该是很典型的分布式事务吧,补偿机制?
    qza1212
        2
    qza1212  
       2022-01-15 21:59:03 +08:00
    感觉顺序就错了,应该先创建订单,然后再扣积分
    JasonLaw
        3
    JasonLaw  
    OP
       2022-01-15 22:06:50 +08:00
    @qza1212 #2 创建订单包含了扣减积分,扣减积分的结果会影响订单的状态。
    qza1212
        4
    qza1212  
       2022-01-15 22:21:14 +08:00
    @JasonLaw #3 了解了,两个方案吧,要么把积分扣减和将订单改为已支付这两个操作写到一个事务里,这里必然要用分布式事务。
    或者也可以在扣减积分记录里记下订单 ID ,这样查询订单是未支付的时候需要再查一下积分记录,查看是否真的扣减成功。
    dzdh
        5
    dzdh  
       2022-01-15 22:24:42 +08:00
    经典事物补偿
    LinSP
        6
    LinSP  
       2022-01-15 22:25:49 +08:00
    用分布式事务感觉重了,扣减积分和订单对应一下就行,订单失败了就补偿下
    dzdh
        7
    dzdh  
       2022-01-15 22:26:02 +08:00
    不会出现"扣减了积分,但是没有成功创建订单"的情况。
    只会出现事物超时下单失败,积分重新+回去。
    gancl
        8
    gancl  
       2022-01-15 23:28:49 +08:00
    可以积分先锁住,创建订单失败则解锁积分;创建订单成功则用掉锁住的积分吗?
    akira
        9
    akira  
       2022-01-15 23:55:47 +08:00
    扣减 改为冻结,订单成功后才真的扣
    clf
        10
    clf  
       2022-01-16 01:37:26 +08:00
    创建订单-订单中就包含了需要消耗的积分和会员 ID

    扣减积分-扣减记录中包含了订单的 ID

    当订单和积分均存在时,这两条数据才是订单创建成功+积分扣除成功;如果不存在对应数据,用户端两条数据均不会显示,并触发回滚机制(触发回滚后提醒用户失败原因)
    Rocketer
        11
    Rocketer  
       2022-01-16 02:14:34 +08:00 via iPhone
    我记得在某银行的积分商城里见过这个场景,积分记录里有创建订单扣减和取消订单(其实是订单失败)增加两条记录
    levelworm
        12
    levelworm  
       2022-01-16 03:06:53 +08:00 via Android
    @LinSP 请教一下,这有似乎个问题,如果补偿的时候商品卖完了怎么办?感觉还是强制要求确认订单成功之后再扣减积分用户体验比较好?不过如果扣减不成功,补偿也会出现万一用户下单多次导致补偿的时候无分可扣,还得取消多余的订单。。。是不是我想的太多了。。。
    levelworm
        13
    levelworm  
       2022-01-16 03:08:14 +08:00 via Android
    @akira 我感觉这个还蛮好,同时避免了用户连续下单导致积分来不及扣。看来冻结是必要的。
    winglight2016
        14
    winglight2016  
       2022-01-16 09:27:15 +08:00
    @JasonLaw 不一定非要用分布式事务来解决。一是可以用工作流来处理,二是可以用 MQ 半事务,分布式事务个人觉得不可靠,是最后的选择。
    751327
        15
    751327  
       2022-01-16 17:38:21 +08:00   ❤️ 1
    本地消息表、事务消息、tcc 方案、Seata AT
    beitayongguo
        16
    beitayongguo  
       2022-01-17 11:50:47 +08:00
    业务上创建订单强依赖扣减积分 TCC 可能好点。 如果不强依赖,那搞成异步 MQ 吧~ 本地消息表事务消息都可以
    julyclyde
        17
    julyclyde  
       2022-01-17 12:28:43 +08:00
    其实积分场景用事后对账机制挺好的
    TUNGH
        18
    TUNGH  
       2022-01-17 12:38:11 +08:00
    我说一个解决方案吧,先创建订单,订单状态是中间态,比如支付中或者处理中,然后去扣积分,扣除成功就改订单支付成功状态,扣除失败就改订单状态为创建失败,失败原因是积分不足.当然,如果是积分和现金一起支付,那必然先锁住积分再创建订单.
    eijnix
        19
    eijnix  
       2022-01-17 16:36:09 +08:00
    巧了 我就是某电商公司做会员的。。
    我们是这样做的,积分表有个预扣字段,首先进行预扣,并记录需要下订单的商品的唯一 id ,再进行下订单,下订单失败则进行积分回滚,否则就进行积分正式扣除(核对下订单的商品的唯一 id )
    当然这里会发一些 kafka 给重试服务,是为了防止服务 down 掉的情况
    eijnix
        20
    eijnix  
       2022-01-17 16:41:49 +08:00
    你现在的流程的话已经跟商城服务有太多的耦合了,而且用户的入口似乎是商城那边,做法可能跟我这个就不太一样了,有点像我们会员中的 offer 服务,我们用的是 tcc 方案
    x66
        21
    x66  
       2022-01-17 17:54:54 +08:00
    @TUNGH 事实上你的方案没有解决扣减积分和修改订单状态不在同一个事务里的问题。
    如果扣减积分成功,修改订单状态失败,还是需要一个补偿机制。
    TUNGH
        22
    TUNGH  
       2022-01-18 08:03:33 +08:00 via Android
    @x66 嗯嗯,确实
    vibbow
        23
    vibbow  
       2022-01-19 02:26:15 +08:00
    先扣积分,然后创建订单。

    之后检查订单是否创建成功,失败的话退回积分。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1624 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 17:01 · PVG 01:01 · LAX 09:01 · JFK 12:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.