V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
dzdh
V2EX  ›  问与答

分布式事务中某些节点必须成功的怎么办

  •  
  •   dzdh · 2021-11-10 15:51:02 +08:00 · 1039 次点击
    这是一个创建于 869 天前的主题,其中的信息可能已经有所发展或是发生改变。

    比如经典的转账场景

    a , 扣 10 成功 b , 加 10 成功 c , 调用银行接口(外部系统)出款成功

    如果 b 失败了 a,c 成功了。由于 C 无法回滚。A/B 必须成功,这种场景怎么办。

    10 条回复    2021-11-10 18:55:43 +08:00
    dongfuye1
        1
    dongfuye1  
       2021-11-10 15:58:30 +08:00
    你可以这么设计,C 必须等待 AB 成功之后才能执行,这样保证了这个转账事务的 C 不需要回滚,只要 AB 完成后,就一定成功。

    我开源的分布式事务框架 https://github.com/yedf/dtm ,里面有一个并发 saga ,可以很方便的解决你的问题
    dzdh
        2
    dzdh  
    OP
       2021-11-10 16:03:14 +08:00
    @dongfuye1 那要是多个外部系统呢都不可控呢
    kop1989
        3
    kop1989  
       2021-11-10 16:11:58 +08:00
    @dzdh #2
    根据需求每分钟 /小时 /日定时做自动审计、对账工作。
    回滚只是一种异常情况下的逆处理,但未见得所有业务都支持逆向处理,这时候就需要审计程序来擦屁股。
    kop1989
        4
    kop1989  
       2021-11-10 16:13:12 +08:00
    @dzdh #2
    这也是为什么很多重要业务的成功节点是“xx 分钟”、“24 小时”、“三个工作日”。
    dzdh
        5
    dzdh  
    OP
       2021-11-10 16:18:48 +08:00
    @kop1989

    也就是说比如两个外部系统+一个本地系统的场景:

    1. 本地系统记账扣了多少钱从 xx 经销商
    2. 调用外部系统(银行)从 xx 供货商户头扣款
    3. 调用物流系统发货

    如果本地记账成功,扣款成功发货失败。
    xx 单位时间后 全局扫单吗 是扣退回款项还是补发货

    如果本地记账成功,扣款失败发货成功
    xx 单位时间后再扫单?

    那假设一种极端场景 外部系统之间相互冲突还都不能撤销 只能自己业务承担损失?
    dongfuye1
        6
    dongfuye1  
       2021-11-10 16:20:36 +08:00
    @dzdh 我接触过的外部系统 api ,大致有以下三种情况:
    1. 一定能够执行成功,不支持回滚操作
    2. 业务有可能失败,也支持回滚操作
    3. 业务有可能失败,又不支持回滚操作

    大部分属于 1 、2 ,极少碰见 3 ,因为 3 这种是没办法协作的。一般像出款这类的业务,会通过 TCC 这种模式,先预留资源,变成 2 这种。
    kop1989
        7
    kop1989  
       2021-11-10 16:27:24 +08:00
    @dzdh #5
    就像#6 说的,“失败,且不支持任何形式的逆操作,甚至不能手动对账审计”其实是极少场景。
    一般来讲,此类场景的做法是做异步结算。

    保证此场景一定是脱离主流程,且可容错的。
    akiasprin
        8
    akiasprin  
       2021-11-10 18:21:15 +08:00
    普通的 2PC 感觉就可以?
    第一步协调者让 ABC 在本地把操作记录下来,并询问是否可以继续执行;
    ABC 返回结果后,进行第二步,协调者执行同步操作,让 ABC 把结果记录到本地,然后强制必须执行(第一步的询问过程后,ABC 都必须暂停对共享资源的其他事务处理,所以这次执行与第一步询问时所有状态是一致的)。
    akiasprin
        9
    akiasprin  
       2021-11-10 18:23:27 +08:00
    @akiasprin 不过是外部系统,这个过程不可控的话,2PC 也不行。要么构建依赖同步关系(先 AB 成功再 C ),要么只能离线跑审计了。
    gamexg
        10
    gamexg  
       2021-11-10 18:55:43 +08:00
    如果完全都不支持事务操作,那么这么实现:

    先执行 a ,扣款操作。确定 a 成功后,再执行 b 操作。确定 b 操作成功,在执行 c 操作。

    转账原则是,先执行扣款(有利于在自己的操作),后执行加款(有利于对方的操作),防止被人找到漏洞刷钱。

    你可以信任自己公司信誉,发现扣款,但是没给对方加款的话,你们公司会补偿对方。例如退款或者再次加款。

    但是不要信任对方,即使你目前的客户都是可信的,但是被人发现这个问题后,自会有专门干这行的去刷你的漏洞。

    对于失败,尝试重试,还是失败,这回滚之前的操作。例如,转账目标账号不存在,那么给来源账号加回去。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1194 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 18:24 · PVG 02:24 · LAX 11:24 · JFK 14:24
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.