( 十六 )、SpringBoot 多數據源分布式事務 之 Spring-boot-starter-jta-atomikos


( 十六 )、SpringBoot 多數據源分布式事務 之 Spring-boot-starter-jta-atomikos

 

 

1、簡介

 這種情況適用於在一個項目中但是有多個數據源的情況,如果是微服務的分布式事務則不建議用這種方式,主要是因為這種方式是阻塞的。

XA 事務的基礎是兩階段提交協議。分為以下兩階段:

  1. 需要有一個事務協調者來保證所有的事務參與者都完成了准備工作。
  2. 如果協調者收到所有參與者都准備好的消息,就會通知所有的事務都可以提交了。

 Mysql 在這個XA事務中扮演的是參與者的角色,而不是協調者(事務管理器)。

第一階段(准備階段):
協調者向參與者發起指令,參與者評估自己的狀態,如果參與者評估指令可以完成,則會寫redo或者undo日志,讓后鎖定資源,執行操作,但並不提交。
第二階段:
如果每個參與者明確返回准備成功,則協調者向參與者發送提交指令,參與者釋放鎖定的資源,如何任何一個參與者明確返回准備失敗,則協調者會發送中指指令,參與者取消已經變更的事務,釋放鎖定的資源。

兩階段提交方案應用非常廣泛,幾乎所有商業OLTP數據庫都支持XA協議。但是兩階段提交方案鎖定資源時間長,對性能影響很大,基本不適合解決微服務事務問題。

缺點:如果協調者宕機,參與者沒有協調者指揮,則會一直阻塞。

三階段提交協議
三階段提交協議是兩階段提交協議的改進版本。它通過超時機制解決了阻塞的問題,並且把兩個階段增加為三個階段:

詢問階段:協調者詢問參與者是否可以完成指令,協調者只需要回答是還是不是,而不需要做真正的操作,這個階段超時導致中止。

准備階段:如果在詢問階段所有的參與者都返回可以執行操作,協調者向參與者發送預執行請求,然后參與者寫redo和undo日志,執行操作,但是不提交操作;如果在詢問階段任何參與者返回不能執行操作的結果,則協調者向參與者發送中止請求,這里的邏輯與兩階段提交協議的的准備階段是相似的,這個階段超時導致成功

提交階段:如果每個參與者在准備階段返回准備成功,也就是預留資源和執行操作成功,協調者向參與者發起提交指令,參與者提交資源變更的事務,釋放鎖定的資源;如果任何一個參與者返回准備失敗,也就是預留資源或者執行操作失敗,協調者向參與者發起中止指令,參與者取消已經變更的事務,執行undo日志,釋放鎖定的資源,這里的邏輯與兩階段提交協議的提交階段一致

 2PC與3PC提交區別:
三階段提交協議與兩階段提交協議相比,優點:增加了一個詢問階段,詢問階段可以確保盡可能早的發現無法執行操作而需要中止的行為,但是它並不能發現所有的這種行為,只會減少這種情況的發生在准備階段以后,協調者和參與者執行的任務中都增加了超時,一旦超時,協調者和參與者都繼續提交事務,默認為成功,這也是根據概率統計上超時后默認成功的正確性最大

但是一旦發生超時,系統仍然會發生不一致,只不過這種情況很少見罷了,好處就是至少不會阻塞和永遠鎖定資源。

2、maven依賴

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jta-atomikos</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

 

3、Yml配置 數據源

mysql: datasource: db1: url: jdbc:mysql://localhost:3306/test01
 username: root password: 123456 driverClassName: com.mysql.cj.jdbc.Driver minPoolSize: 3 maxPoolSize: 25 maxLifetime: 20000 mborrowConnectionTimeout: 30 loginTimeout: 30 maintenanceInterval: 60 maxIdleTime: 60 db2: url: jdbc:mysql://localhost:3306/test02
 username: root password: 123456 driverClassName: com.mysql.cj.jdbc.Driver minPoolSize: 3 maxPoolSize: 25 maxLifetime: 20000 mborrowConnectionTimeout: 30 loginTimeout: 30 maintenanceInterval: 60 maxIdleTime: 60

 

4、讀取配置到Bean

DB1Config、DB2Config

@ConfigurationProperties(prefix = "mysql.datasource.db1") @Component public class DB1Config { private String url; private String username; private String password; private int minPoolSize; private int maxPoolSize; private int maxLifetime; private int borrowConnectionTimeout; private int loginTimeout; private int maintenanceInterval; private int maxIdleTime; private String driverClassName; private String testQuery; } @ConfigurationProperties(prefix = "mysql.datasource.db2") @Component public class DB2Config { private String url; private String username; private String password; private int minPoolSize; private int maxPoolSize; private int maxLifetime; private int borrowConnectionTimeout; private int loginTimeout; private int maintenanceInterval; private int maxIdleTime; private String driverClassName; private String testQuery; }

 

5、數據源配置

DB1MybatisConfig、DB2MybatisConfig 
@Configuration @MapperScan(basePackages = "com.dw.study.mapper.db1", sqlSessionTemplateRef = "db1SessionTemplate") public class DB1MybatisConfig { @Autowired private DB1Config db1Config; @Bean("db1DataSource") public DataSource db1DataSource() throws SQLException { DruidXADataSource druidXADataSource = new DruidXADataSource(); druidXADataSource.setUrl(db1Config.getUrl()); druidXADataSource.setPassword(db1Config.getPassword()); druidXADataSource.setUsername(db1Config.getUsername()); druidXADataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean(); xaDataSource.setUniqueResourceName("db1DataSource"); xaDataSource.setMinPoolSize(db1Config.getMinPoolSize()); xaDataSource.setMaxPoolSize(db1Config.getMaxPoolSize()); xaDataSource.setMaxLifetime(db1Config.getMaxLifetime()); xaDataSource.setBorrowConnectionTimeout(db1Config.getBorrowConnectionTimeout()); xaDataSource.setLoginTimeout(db1Config.getLoginTimeout()); xaDataSource.setMaintenanceInterval(db1Config.getMaintenanceInterval()); xaDataSource.setMaxIdleTime(db1Config.getMaxIdleTime()); xaDataSource.setXaDataSource(druidXADataSource); return xaDataSource; } @Bean(name = "db1SimpleSessionFactory") public SqlSessionFactory db1SimpleSessionFactory(@Qualifier("db1DataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(); bean.setMapperLocations(resourceResolver.getResources("classpath:mapper/db1/*.xml")); bean.setTypeAliasesPackage("com.dw.study.pojo.*"); org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); configuration.setMapUnderscoreToCamelCase(true); bean.setConfiguration(configuration); return bean.getObject(); } @Bean(name = "db1SessionTemplate") public SqlSessionTemplate db1SessionTemplate( @Qualifier("db1SimpleSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } } @Configuration @MapperScan(basePackages = "com.dw.study.mapper.db2",sqlSessionTemplateRef = "db2SessionTemplate") public class DB2MybatisConfig { @Autowired private DB2Config DB2Config; @Bean("db2DataSource") public DataSource db2DataSource() throws SQLException { DruidXADataSource druidXADataSource = new DruidXADataSource(); druidXADataSource.setUrl(DB2Config.getUrl()); druidXADataSource.setPassword(DB2Config.getPassword()); druidXADataSource.setUsername(DB2Config.getUsername()); druidXADataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean(); xaDataSource.setUniqueResourceName("db2DataSource"); xaDataSource.setMinPoolSize(DB2Config.getMinPoolSize()); xaDataSource.setMaxPoolSize(DB2Config.getMaxPoolSize()); xaDataSource.setMaxLifetime(DB2Config.getMaxLifetime()); xaDataSource.setBorrowConnectionTimeout(DB2Config.getBorrowConnectionTimeout()); xaDataSource.setLoginTimeout(DB2Config.getLoginTimeout()); xaDataSource.setMaintenanceInterval(DB2Config.getMaintenanceInterval()); xaDataSource.setMaxIdleTime(DB2Config.getMaxIdleTime()); xaDataSource.setXaDataSource(druidXADataSource); return xaDataSource; } @Bean(name = "db2SessionFactory") public SqlSessionFactory lyjSessionFactory(@Qualifier("db2DataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(); bean.setMapperLocations(resourceResolver.getResources("classpath:mapper/db2/*.xml")); bean.setTypeAliasesPackage("com.dw.study.pojo.*"); org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); configuration.setMapUnderscoreToCamelCase(true); bean.setConfiguration(configuration); return bean.getObject(); } @Bean(name = "db2SessionTemplate") public SqlSessionTemplate db2SessionTemplate( @Qualifier("db2SessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM