前言:
前面学习了什么是分布式事务,在具体的工程中我们应该怎么样使用JTA来实现分布式事务呢?当没有引入事务时,如果使用多个数据源,发现在切换数据源后可以正常的执行,但是如果添加了事务注解之后呢,发现不管怎么切换数据源都不管用了。原因就是因为事务管理器,默认的事务管理器是不能切换的,如果开始切换成功data1,那么程序获取了connection后是不会释放的,直到提交才会释放,因此当切换到data2时,使用的其实还是刚才data1获取到的connection,因此不管怎么切换都不管用。如果加入JTA事务管理器就可以正常切换了,发生异常回滚时也可以将两个数据库的内容都回滚。
如何配置动态数据源
spring-jdbc包中提供了一个AbstractRoutingDataSource类,该类中有一个抽象方法determineCurrentLookupKey,如果自定义类继承了AbstractRoutingDataSource类并且实现determineCurrentLookupKey即可实现数据源的自动切换。
JTA原理
JTA的原理是利用XA协议来实现的,一般数据库厂商都会支持XA协议,比如mysql:
上图中可以看出只有innoDB支持XA协议,如果没有XA协议程序中使用JTA是不生效的,因此就又引申出一个概念--分布式数据库。什么是分布式数据库,最大的区别有两个:
1、数据库支不支持横向动态扩展。
2、事务能不能支持处理,比如使用mysql搭建一个分布式数据库,那么这些事务就需要应用自身去协调处理。而分布式数据库对于应用而言和单体数据库是没有任何区别的,分布式事务会由数据库自身去解决。
分布式跨库事务解决方案
因为我目前所维护的系统是基于某国产化数据 + oracle1 + oracle2这样的数据库组合。对于分布式事务,如果采用这种JTA的形式,就要保证三个数据库全部支持XA协议,但是很遗憾该组合中的国产化数据库并不支持XA,因此系统本身也没有集成JTA等分布式事务处理方式,而是通过业务层面的业务规则来有效的避免了这种分布式事务。对于一些实在不可避免的同一个事务中既操作A库,又操作B库的重要业务,系统提供了类似数据稽核的纠错机制,这样可有效保证最终的数据一致性。
看了一些资料,还有一种分布式事务解决方案是类似本地消息表的方式,比如在分库(当前库 + 历史库)场景下,一个业务可能既要记录数据到当前库,同时还要记录一条到历史库,那么在开始执行操作当前库时就会产生事务编号A1,将事务编号记录到当前库(特定表)中,同时记录需要在历史库中执行的sql以及数据源标识,接下来当前库操作完成提交事务,如果成功,则继续在历史库记录A1事务编号并提交事务,如果历史库操作失败。此时当前库记录的A1为提交,而历史库事务记录表中没有A1的记录。框架提供事务稽核程序,用来扫描各个数据库中的记录表,如果遇到上述情况,则由稽核程序来获取当前库中事务记录表中的sql、源数据库标识、目标数据库标识,通过告警的方式通知维护人员进行手动处理,通过补偿机制来保证最终数据的一致性。
过程可以分为如下:
1、开始数据库操作
2、记录主库事务编号、sql、sourceDbkey、targetDbKey等,如果记录失败则回滚事务
3、记录分库事务,如果此时失败,则回滚分库事务。
4、稽核程序扫描各个库事务记录表,比对事务记录事务ID,如果发现主库中存在事务ID,对应的目标数据库中没有该事务ID,则认为存在数据不一致性,需要通知维护人员处理。
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第24天,点击查看活动详情