美文网首页
事务中动态切换数据源的问题

事务中动态切换数据源的问题

作者: 维特无忧堡 | 来源:发表于2018-08-29 11:14 被阅读0次

    前言

    前几天在做一个项目的时候遇到的一个问题,在事务中动态切换数据源报错,于是上网百度了一下@Transaction注解的执行逻辑,然后才恍然大悟....

    调用链

    事务代码调用链:
    service注解上@transactional-->TransactionInterceptor.interpter()-->TransactionAspectSupport.createTransactionIfNecessary()-->AbstractPlatformTransactionManager.getTransaction()-->DataSourceTransactionManager.doBegin()-->AbstractRoutingDataSource.determineTargetDataSource()[lookupKey==null去拿默认的Datasource, 不为空则使用获取到的连接]
    -->DataSourceTransactionManager.setTransactional()[将连接设置到TransactionUtils的threadLocal中]
    --->Repository@Annotation-->执行一般调用链, 问题在于SpringManagedTransaction.getConnection()
    -->openConnection()
    -->DataSourceUtils.getConnection()
    -->TransactionSynchronizationManager.getResource(dataSource)不为空[从TransactionUtils的threadLocal中获取数据源], 所以不会再去调用DynamicDataSource去获取数据源

    问题分析与解决

    从这里可以看出来,每次都会从threadLocal中去拿数据源,如果为空,就创建新的连接,如果不为空的话直接复用,而threadLocal是线程型的变量,只要在同一个中,就只会存在一个,所以这就会导致你切换数据源失败,因为threadLocal不为空。

    解决方案

    你可以把threadLocal变量给remove掉,不过threadLocal是private是私有的,可以根据反射修改,网上有一套解决方案,有点麻烦。

    因为我的场景是在一个事务中mysql写,而sqlserver读,所以sqlserver实质上不需要事务,所以我新开了一个线程来获取sqlserver的值,搭配futureTask来获取异步的返回值(get阻塞),这样就可以实现了(因为新开了一个线程之后threadLocal就是null了

    这里还会存在一个问题,假设你使用的是线程连接池的话,还要考虑一下工作线程复用的情况(这样也会导致threadLocal不为空)

    最好的办法应该就是分布式事务的处理,不过我还不太了解...

    相关文章

      网友评论

          本文标题:事务中动态切换数据源的问题

          本文链接:https://www.haomeiwen.com/subject/ljkhwftx.html