AbstractRoutingDataSource数据源动态切换跨库事物失效问题解决方案

Posted OkidoGreen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AbstractRoutingDataSource数据源动态切换跨库事物失效问题解决方案相关的知识,希望对你有一定的参考价值。

完整代码:GitHub - duanbin0414/dynamic-datasource-boot: 数据源动态切换事务支持

数据源动态切换、跨库事物失效问题解决方案

一、问题出现的场景

系统架构设计、每个企业一个企业库、通过数据源切在平台库、和企业库之间动态切换完成业务操作。

二、跨库事物失效的原因

1、Spring@Transactional不支持跨数据源事物,Spring 事物控制是基于数据库链接进行的,当数据源切换后,数据库链接切换,事物回滚只能回退,当前持有的链接。
2、Spring开启事物后,会将当前数据库及数据库链接资源进行线程绑定,导致数据源切换失效(数据源切换执行后,并未获取到新的数据库链接)。

三、解决方案

基于InheritableThreadLocal + aop 针对添加了跨库事务注解的方法进行拦截、线程标记、覆写mybatis自动提交,通过aop手动提交/回退,实现跨库事务。

1、自定义跨库事物注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MultiTransactional 

2、对有标记的请求、保存数据库链接、关闭自动提交


在动态数据源获取数据库链接处特殊处理,如果开启了跨库事物,则返回包装后对connection对象(包装主要为了避免mybatis自动提交和回滚)、并且将对应的数据库链接保存到线程上下文。

3、对开启了跨库事物方法进行拦截、标记、提交/回滚

基于InheritableThreadLocal 对添加了跨库事物注解@MultiTransactional的方法进行拦截
1)在方法执行前:设置multilTransactionalStatus = true;
2)在方法执行异常时:从当前线程上线文获取到所有链接、统一rollback();
3)在方法正常执行结束:从当前线程上线文获取到所有链接、统一commit();

四、注意事项

1、使用InheritableThreadLocal(带继承)能保证线程标记不丢失。
2、需要对执行手动事物的链接对象包装、覆写submit()、rollback()方法、否则mybatis在sql执行后会自动提交,导致事物失败。
3、请求执行结束后(包含正常、异常情况)均要提交/回滚事物、释放资源,避免内存泄漏、链接异常。
4、本文使用了ThreadLoca保存链接,同理也可使用HttpServletRequest对象保存链接资源(会在定时任务等特殊场景下失效),可酌情考虑使用。

五、压测报告

对同一段代码进行三种事务控制: 不添加事务、添加@Transactional、添加@MultiTransactional 进行压测对比。

无事务控制

添加Transactional

添加MultiTransactional
无事务控制
添加Transactional

添加MultiTransactional

无事务控制
添加Transactional

添加MultiTransactional

注:所有异常情况均为 429 Too Many Requests (太多请求)

结论:解决方案性能ok

经过三种事务控制压测结果对比,MultiTransactional注解方式支持跨库事务、没有带来额外的性能压力。

以上是关于AbstractRoutingDataSource数据源动态切换跨库事物失效问题解决方案的主要内容,如果未能解决你的问题,请参考以下文章

AbstractRoutingDataSource - 动态数据源

AbstractRoutingDataSource实现动态数据源切换 专题

切换数据库+ThreadLocal+AbstractRoutingDataSource

带有注释和(动态)AbstractRoutingDataSource 的 Spring 3.1.3 + Hibernate 配置

AbstractRoutingDataSource 实现动态数据源切换原理简单分析

AbstractRoutingDataSource 在运行时更改映射