Spring 多数据源事务问题

2019-10-21 17:18:45 +08:00
 dackh

今天遇到一个场景,需要 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,是不是可以呢?

4811 次点击
所在节点    程序员
16 条回复
simonlu9
2019-10-21 18:28:53 +08:00
看看源码,之前有印象 spring 有一个事务链来维护的,新的事务在旧的事务前面,如果 updagteB 发生异常,updaeA 肯定能捕获,肯定会回滚,但是你 updateA 后面有异常,updateB 不可能回滚
pws22
2019-10-21 18:46:14 +08:00
d e 如果出错 你 updateB 应该不会回滚,你要在 controller(或者另一个 service)里面去调用 updateA,updateB,如果在 updateA 里面调用 updateB 的话,updateB 不会加入事务的,因为这时候你的 updateB 没有被代理,如果我没记错的话
jetyang
2019-10-21 18:53:59 +08:00
分布式事务
lovelife1994
2019-10-21 19:05:19 +08:00
这种只支持本地事务吧,要做到同时提交回滚需要基于同一个连接的,分布式全局事务 spring 也是支持的。
nosilence
2019-10-21 20:16:35 +08:00
事务是基于连接的,不同的数据源需要分布式事务。
sun1993
2019-10-21 20:44:54 +08:00
首先 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 不高的话可以考虑,使用起来非常简单)。
Aruforce
2019-10-22 08:15:43 +08:00
@sun1993 aop 基于代理的时候才会出现你说的 B 不回滚这种情况……现在是跨数据源了…单纯 Spring 搞不定的…
HiShan
2019-10-22 09:10:33 +08:00
我也遇到了这个问题,麻烦楼主解决了告诉我一下吧,谢谢~
Ianchen
2019-10-22 09:36:24 +08:00
分布式事务 或 改队列
lachesis
2019-10-22 10:11:36 +08:00
不同数据源需要分布式事务
q4487979711
2019-10-22 10:19:10 +08:00
不同数据源用 atomikos
HiShan
2019-10-22 10:27:39 +08:00
hantsy
2019-10-22 11:13:16 +08:00
@q4487979711 没错。
1. 单独的 Spring 程序( jar, 运行 Tomcat 等),多数据源 DataSource,任何 XA Resource(如 JMS 等), 可以用 atomikos 类似的支持 JTA 方案。
2. 如果你的 Spring 应用程序( war )运行于标准的应用服务器,如 Wildfly 等,事务可以 Delegate 到容器事务处理。
3. 如果 Microservice 架构,这种问题应该很常见,几乎都会遇到。可以借助一些工作流方案,或者纯消息( Kafka,RabbitMQ ),每一步加上一个 Compensation 回路,应对失败补偿的情况。实现这个过程有点复杂。
dackh
2019-10-22 18:09:03 +08:00
@sun1993 AopContext.currentProxy() 可以解决同类内调用事务 aop 代理不生效问题,我这种方法其实类似二段式,存在 updateB 成功了,但是 updateA 在 commit 失败了的问题,以及二段式的阻塞问题跟过于保守的问题,如果不在意性能的话,我觉得这种处理是没问题的,毕竟完全保证一致性无法通过简单的方法去实现。


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


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


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


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


@HiShan 先 mark,晚点看下


@hantsy 我昨天看了一种好像叫做 TCC 的方案,也是补偿的方案
hantsy
2019-10-22 19:01:31 +08:00
@dackh TCC 似乎源于 atomikos,这个我不确定,没有深入研究。

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

但是,对于复杂的应用,NoSQL,消息,RDBMS,等都用上的时候,感觉传统的事务难以实现数据一致性,ACID 让位于 CAP,只有借助一些设计模式( Saga ),状态机(工作流)去实现的业务模式。
sun1993
2019-10-23 12:19:29 +08:00
@dackh 嗯,currentProxy 是可以处理 aop 问题的(后来审题发现你已经说明 updateA 和 updateB 不同类了,我审题有问题,(。・_・。)ノ I’m sorry~),针对你给出的条件,我上面的回答也指出了,俩都是可以回滚的,确实是没问题的,不过涉及分布式事务的话,还是利用现有的技术实际解决下吧,q4487979711 提到的 atomikos 是 XA 协议的一种实现,就是你说的两段式提交,可以从根本上解决异源数据库的事务问题

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/611462

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX