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

Spring 多数据源事务问题

  •  
  •   dackh · 46 天前 · 1177 次点击
    这是一个创建于 46 天前的主题,其中的信息可能已经有所发展或是发生改变。

    今天遇到一个场景,需要 update 多个数据源的数据。

    假如数据源 A,有三天 update 语句分别为 a,b,c,数据源 B 的 update 语句为 d,e。

    配置了两个transactionManagerAtransactionManagerB分别对应数据源 A 跟数据源 B。

    我在 service 中的代码例子:

    
    @Transactional(transationManager = "transactionManagerA",rollback = Exception.class)
    public void updateA(){
    	a();
        b();
        c();
        updateB();
    }
    
    //另一个类或者 AopContext.currentProxy() 
    @Transactional(transationManager = "transactionManagerB",rollback = Exception.class)
    public void updateB(){
    	d();
        e();
    }
    

    这样场景下,假如 d 或者 e 出现异常,那么 updateA 也将会 rollback,是不是可以呢?

    16 回复  |  直到 2019-10-23 12:19:29 +08:00
        1
    simonlu9   46 天前
    看看源码,之前有印象 spring 有一个事务链来维护的,新的事务在旧的事务前面,如果 updagteB 发生异常,updaeA 肯定能捕获,肯定会回滚,但是你 updateA 后面有异常,updateB 不可能回滚
        2
    pws22   46 天前   ♥ 1
    d e 如果出错 你 updateB 应该不会回滚,你要在 controller(或者另一个 service)里面去调用 updateA,updateB,如果在 updateA 里面调用 updateB 的话,updateB 不会加入事务的,因为这时候你的 updateB 没有被代理,如果我没记错的话
        3
    jetyang   46 天前
    分布式事务
        4
    lovelife1994   46 天前 via iPhone
    这种只支持本地事务吧,要做到同时提交回滚需要基于同一个连接的,分布式全局事务 spring 也是支持的。
        5
    nosilence   46 天前
    事务是基于连接的,不同的数据源需要分布式事务。
        6
    sun1993   46 天前   ♥ 1
    首先 spring 是基于 aop 做的事务管理,也就是说 updateB 如果跟 updateA 在同一个类里,那么 updateB 出现异常的话,updateB 是不会回滚的,因为 updateB 是本类调用,无法 aop 代理,但是 updateA 里捕获到了 updateB 的异常,所以 updateA 的 a,b,c 操作是可以顺利回滚的。
    如果 updateA 和 updateB 处于不同类,那么 updateB 异常,updateA 和 updateB 都会回滚。
    但上面只是针对你这个例子,这种到底还是分布式事务,所以还是存在下面的问题:
    如果 updateA 在调用 updateB 之后下面还有别的操作导致 updateA 异常了,那么 updateB 是不会回滚的,但是 updateA 会回滚。
    事务的回滚和提交都是基于同一个连接对象的,多数据源意味着有多个连接对象,这就是典型的分布式事务,这种事务问题最好借助 XA 协议来完成( XA 有一定的性能问题,如果 qps 不高的话可以考虑,使用起来非常简单)。
        7
    Aruforce   45 天前 via Android
    @sun1993 aop 基于代理的时候才会出现你说的 B 不回滚这种情况……现在是跨数据源了…单纯 Spring 搞不定的…
        8
    HiShan   45 天前
    我也遇到了这个问题,麻烦楼主解决了告诉我一下吧,谢谢~
        9
    Ianchen   45 天前
    分布式事务 或 改队列
        10
    lachesis   45 天前
    不同数据源需要分布式事务
        11
    q4487979711   45 天前
    不同数据源用 atomikos
        13
    hantsy   45 天前
    @q4487979711 没错。
    1. 单独的 Spring 程序( jar, 运行 Tomcat 等),多数据源 DataSource,任何 XA Resource(如 JMS 等), 可以用 atomikos 类似的支持 JTA 方案。
    2. 如果你的 Spring 应用程序( war )运行于标准的应用服务器,如 Wildfly 等,事务可以 Delegate 到容器事务处理。
    3. 如果 Microservice 架构,这种问题应该很常见,几乎都会遇到。可以借助一些工作流方案,或者纯消息( Kafka,RabbitMQ ),每一步加上一个 Compensation 回路,应对失败补偿的情况。实现这个过程有点复杂。
        14
    dackh   45 天前
    @sun1993 AopContext.currentProxy() 可以解决同类内调用事务 aop 代理不生效问题,我这种方法其实类似二段式,存在 updateB 成功了,但是 updateA 在 commit 失败了的问题,以及二段式的阻塞问题跟过于保守的问题,如果不在意性能的话,我觉得这种处理是没问题的,毕竟完全保证一致性无法通过简单的方法去实现。


    @pws22 将 updateB 放在另一个类或者 AopContext.currentProxy()可以解决,我在代码注释已经加了的


    @HiShan 如果不在性能的话,我简单这种方案是可以的,只要最终一致性可以通过 mq 来实现。


    @q4487979711 这个我还没研究过,晚点有空看下


    @Aruforce 两个数据源对应两个事务管理器的


    @HiShan 先 mark,晚点看下


    @hantsy 我昨天看了一种好像叫做 TCC 的方案,也是补偿的方案
        15
    hantsy   45 天前
    @dackh TCC 似乎源于 atomikos,这个我不确定,没有深入研究。

    https://www.atomikos.com/Blog/TransactionManagementAPIForRESTTCC

    但是,对于复杂的应用,NoSQL,消息,RDBMS,等都用上的时候,感觉传统的事务难以实现数据一致性,ACID 让位于 CAP,只有借助一些设计模式( Saga ),状态机(工作流)去实现的业务模式。
        16
    sun1993   44 天前
    @dackh 嗯,currentProxy 是可以处理 aop 问题的(后来审题发现你已经说明 updateA 和 updateB 不同类了,我审题有问题,(。・_・。)ノ I’m sorry~),针对你给出的条件,我上面的回答也指出了,俩都是可以回滚的,确实是没问题的,不过涉及分布式事务的话,还是利用现有的技术实际解决下吧,q4487979711 提到的 atomikos 是 XA 协议的一种实现,就是你说的两段式提交,可以从根本上解决异源数据库的事务问题
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   895 人在线   最高记录 5043   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 36ms · UTC 20:38 · PVG 04:38 · LAX 12:38 · JFK 15:38
    ♥ Do have faith in what you're doing.