• 分布式事务之DTP(XA规范)


    DTP(XA规范)

    1. X/Open DTP模型(X/Open Distributed Transaction Processing Reference Model)是X/Open组织定义的一套分布式事务标准,这套标准主要定义了实现分布式事务的规范和API,具体的实现由相应的厂商来实现
    2. XA就是X/Open DTP定义的TM与RM之间的接口规范(即接口函数),TM使用XA通知数据库事务的开始、结束以及提交、回滚等,XA接口函数由数据库厂商实现
    3. X/Open DTP定义了三个组件:AP,TM,RM。
      • AP(Application Program):也就是应用程序,可以理解为使用DTP的程序。
      • RM(Resource Manager):资源管理器,这里可以理解为一个DBMS系统,或者消息服务器管理系统,应用程序通过资源管理器对资源进行控制。资源必须实现XA定义的接口(比如Oracle、Mysql、DB2等数据库)
      • TM(Transaction Manager):事务管理器,负责协调和管理事务,提供给AP应用程序编程接口(TX协议)以及管理资源管理器。
    4. AP可以和TM以及RM通信,TM和RM互相之间可以通信。TM和RM通过XA接口进行双向通信,例如:TM通知RM提交事务或者回滚事务,RM把提交结果通知给TM。AP和RM之间则通过RM提供的Native API(JDBC驱动)进行资源控制
    5. X/Open DTP定义的概念
      • 事务:一个事务是一个完整的工作单元,由多个独立的计算任务组成,这多个任务在逻辑上是原子的。
      • 全局事务:一次性操作多个资源管理器的事务,是全局事务。
      • 分支事务:在全局事务中,某一个资源管理器有自己独立的任务,这些任务的集合作为这个资源管理器的分支任务。
      • 控制线程:用来表示一个工作线程,主要是关联AP、TM、RM三者的一个线程,也就是事务上下文环境。简单地说,就是需要标识一个全局事务以及分支事务的关系。

    JTA规范

    1. http://download.oracle.com/otn-pub/jcp/jta-1.1-spec-oth-JSpec/jta-1_1-spec.pdf

    2. Java事务API(JTA:Java Transaction API)和Java事务服务(JTS:Java Transaction Service),为J2EE平台提供了分布式事务服务(distributed transaction)的能力。可以认为JTA规范是XA规范的Java版,其把XA规范中规定的DTP模型交互接口抽象成Java接口中的方法,并规定每个方法要实现什么样的功能。

    3. JTA比XA多了一个Application Server(应用服务器):应用程序运行的容器。JTA规范规定,事务管理器的功能应该由application server提供。并不是所有的web容器都实现了JTA规范,如tomcat并没有实现JTA规范,因此并不能提供事务管理器的功能

    4. Java JTA的接口包

      implementation 'javax.transaction:jta:1.1'
      
      • 1
    5. JTA规范中定义的接口作用(由各个厂商去实现):

      • javax.transaction.Status:事务状态,这个接口主要是定义一些表示事务状态的常量,此接口无需实现
      • javax.transaction.Synchronization:同步
      • javax.transaction.Transaction:事务
      • javax.transaction.TransactionManager:事务管理器
      • javax.transaction.UserTransaction:用于声明一个分布式事务
      • javax.transaction.TransactionSynchronizationRegistry:事务同步注册
      • javax.transaction.xa.XAResource:定义RM提供给TM操作的接口
      • javax.transaction.xa.Xid:事务id

    Atomikos

    1. Atomikos公司官网https://www.atomikos.com/
    2. Atomikos有两个事务管理产品
      • TransactionEssentials:开源的免费产品。
      • ExtremeTransactions:商业版,需要收费。
    3. TransactionEssentials实现了JTA/XA规范中的事务管理器应该实现的相关接口
      • UserTransaction实现是com.atomikos.icatch.jta.UserTransactionImp,用户只需要直接操作这个类
      • TransactionManager实现是com.atomikos.icatch.jta.UserTransactionManager
      • Transaction实现是com.atomikos.icatch.jta.TransactionImp
    4. Atomikos的作用是一个事务管理器™,并不需要提供对应的实现。而Atomikos对XADataSource进行封装,只是为了方便与事务管理器整合。封装XADataSource的实现类为AtomikosDataSourceBean
    5. 典型的XADataSource实现包括:
      • Mysql官方提供的com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
      • 阿里的druid连接池,实现类为com.alibaba.druid.pool.xa.DruidXADataSource
      • Tomcat-jdbc连接池提供的org.apache.tomcat.jdbc.pool.XADataSource

    Atomikos实战

    1. 项目源码:https://github.com/jannal/transaction/tree/master/atomikos-jta

    2. 新建数据库表(Mysql 5.6)

      //1. 创建数据库atomikos_account0
      CREATE DATABASE IF NOT EXISTS atomikos_account0 DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_bin;
      
      USE atomikos_account0;
      
      DROP TABLE IF EXISTS `t_account`;
      CREATE TABLE `t_account`
      (
          `id`             int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
          `account_id`     varchar(50) COLLATE utf8mb4_bin NOT NULL COMMENT '账户标识',
          `amount`         decimal(20, 2)                  NOT NULL DEFAULT '0.00' COMMENT '金额',
          `freezed_amount` decimal(20, 2)                  NOT NULL DEFAULT '0.00' COMMENT '冻结金额',
          `create_time`    datetime                        NOT NULL COMMENT '创建时间',
          `update_time`    datetime                        NOT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
          PRIMARY KEY (`id`),
          UNIQUE KEY `uniq_accout` (`account_id`) USING BTREE
      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='账户表';
      
      INSERT INTO `t_account`(`id`, `account_id`, `amount`, `freezed_amount`, `create_time`, `update_time`)
      VALUES (1, 'jannal', 10000.00, 0.00, '2022-05-03 17:23:37', '2022-05-03 17:23:39');
      
      //2. 创建数据库atomikos_account0
      CREATE DATABASE IF NOT EXISTS atomikos_account1 DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_bin;
      
      USE atomikos_account1;
      
      DROP TABLE IF EXISTS `t_account`;
      CREATE TABLE `t_account`
      (
          `id`             int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
          `account_id`     varchar(50) COLLATE utf8mb4_bin NOT NULL COMMENT '账户标识',
          `amount`         decimal(20, 2)                  NOT NULL DEFAULT '0.00' COMMENT '金额',
          `freezed_amount` decimal(20, 2)                  NOT NULL DEFAULT '0.00' COMMENT '冻结金额',
          `create_time`    datetime                        NOT NULL COMMENT '创建时间',
          `update_time`    datetime                        NOT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
          PRIMARY KEY (`id`),
          UNIQUE KEY `uniq_accout` (`account_id`) USING BTREE
      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='账户表';
      
      INSERT INTO `t_account`(`id`, `account_id`, `amount`, `freezed_amount`, `create_time`, `update_time`)
      VALUES (1, 'tom', 1000.00, 0.00, '2022-05-03 17:23:37', '2022-05-03 17:23:39');
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
    3. 添加项目依赖

      dependencies {
          testAnnotationProcessor('org.springframework.boot:spring-boot-configuration-processor:2.2.6.RELEASE')
          //mybatis
          compile "org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.1",
                  "org.mybatis:mybatis-typehandlers-jsr310:1.0.2",
                  'org.springframework.boot:spring-boot-starter-aop',
                  'mysql:mysql-connector-java:5.1.46'
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    4. 创建数据库配置类

      //主数据源配置(atomikos_account0数据库)
      @ConfigurationProperties(prefix = "spring.datasource.primary")
      @Data
      public class PrimaryDataSourceProperties {
          private String url;
          private String username;
          private String password;
          private int minPoolSize = 2;
          private int maxPoolSize = 10;
          /** max-lifetime 连接最大存活时间 s**/
          private int maxLifetime = 60;
          /** borrow-connection-timeout 获取连接失败重新获等待最大时间s,在这个时间内如果有可用连接,将返回 **/
          private int borrowConnectionTimeout = 20;
          /** login-timeout java数据库连接池,最大可等待获取datasouce的时间s **/
          private int loginTimeout = 30;
          /** maintenance-interval 连接回收时间s **/
          private int maintenanceInterval = 600;
          /** max-idle-time 最大闲置时间s,超过最小连接池连接的连接将将关闭 **/
          private int maxIdleTime = 600;
          /** test-query 测试SQL **/
          private String testQuery = "SELECT 1";
      
      }
      
      //次数据源配置(atomikos_account1数据库)
      @ConfigurationProperties(prefix = "spring.datasource.secondary")
      @Data
      public class SecondaryDataSourceProperties {
        	//与PrimaryDataSourceProperties属性一样
        	...省略....
      
      }
      
      //主数据源配置
      @Configuration
      @EnableConfigurationProperties(PrimaryDataSourceProperties.class)
      @MapperScan(basePackages = {"org.jannal.jta.core.ds0.mapper"}, sqlSessionFactoryRef = "sqlSessionFactory")
      public class PrimaryDataSourceConfiguration {
          @Bean(name = "dataSource")
          @Primary
          public DataSource dataSource(PrimaryDataSourceProperties primaryDataSourceProperties) throws SQLException {
              MysqlXADataSource mysqlXADataSource = new MysqlXADataSource();
              mysqlXADataSource.setUrl(primaryDataSourceProperties.getUrl());
              mysqlXADataSource.setPinGlobalTxToPhysicalConnection(true);
              mysqlXADataSource.setPassword(primaryDataSourceProperties.getPassword());
              mysqlXADataSource.setUser(primaryDataSourceProperties.getUsername());
      
              AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
              atomikosDataSourceBean.setUniqueResourceName("dataSource");
              atomikosDataSourceBean.setXaDataSource(mysqlXADataSource);
              atomikosDataSourceBean.setMinPoolSize(primaryDataSourceProperties.getMinPoolSize());
              atomikosDataSourceBean.setMaxPoolSize(primaryDataSourceProperties.getMaxPoolSize());
              atomikosDataSourceBean.setMaxLifetime(primaryDataSourceProperties.getMaxLifetime());
              atomikosDataSourceBean.setBorrowConnectionTimeout(primaryDataSourceProperties.getBorrowConnectionTimeout());
              atomikosDataSourceBean.setLoginTimeout(primaryDataSourceProperties.getLoginTimeout());
              atomikosDataSourceBean.setMaintenanceInterval(primaryDataSourceProperties.getMaintenanceInterval());
              atomikosDataSourceBean.setMaxIdleTime(primaryDataSourceProperties.getMaxIdleTime());
              atomikosDataSourceBean.setTestQuery(primaryDataSourceProperties.getTestQuery());
              return atomikosDataSourceBean;
      
          }
      
          @Bean(name = "sqlSessionFactory")
          @Primary
          public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) throws Exception {
              SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
              bean.setDataSource(dataSource);
              bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
              org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
              configuration.setLazyLoadingEnabled(true);
              //configuration.setLogImpl(StdOutImpl.class);
              configuration.setMapUnderscoreToCamelCase(true);
              configuration.setDefaultExecutorType(ExecutorType.REUSE);
              configuration.setCacheEnabled(false);
              configuration.setDefaultStatementTimeout(5000);
              bean.setConfiguration(configuration);
              return bean.getObject();
          }
      
          @Bean(name = "sqlSessionTemplate")
          @Primary
          public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
              return new SqlSessionTemplate(sqlSessionFactory);
          }
      
      }
      
      // 次数据源配置
      @Configuration
      @EnableConfigurationProperties(SecondaryDataSourceProperties.class)
      @MapperScan(basePackages = {"org.jannal.jta.core.ds1.mapper"}, sqlSessionFactoryRef = "sqlSessionFactory2")
      public class SecondaryDataSourceConfiguration {
          @Bean(name = "dataSource2")
          public DataSource dataSource(SecondaryDataSourceProperties secondaryDataSourceProperties) throws SQLException {
              MysqlXADataSource mysqlXADataSource = new MysqlXADataSource();
              mysqlXADataSource.setUrl(secondaryDataSourceProperties.getUrl());
              mysqlXADataSource.setPinGlobalTxToPhysicalConnection(true);
              mysqlXADataSource.setPassword(secondaryDataSourceProperties.getPassword());
              mysqlXADataSource.setUser(secondaryDataSourceProperties.getUsername());
      
              AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
              atomikosDataSourceBean.setUniqueResourceName("dataSource2");
              atomikosDataSourceBean.setXaDataSource(mysqlXADataSource);
              atomikosDataSourceBean.setMinPoolSize(secondaryDataSourceProperties.getMinPoolSize());
              atomikosDataSourceBean.setMaxPoolSize(secondaryDataSourceProperties.getMaxPoolSize());
              atomikosDataSourceBean.setMaxLifetime(secondaryDataSourceProperties.getMaxLifetime());
              atomikosDataSourceBean.setBorrowConnectionTimeout(secondaryDataSourceProperties.getBorrowConnectionTimeout());
              atomikosDataSourceBean.setLoginTimeout(secondaryDataSourceProperties.getLoginTimeout());
              atomikosDataSourceBean.setMaintenanceInterval(secondaryDataSourceProperties.getMaintenanceInterval());
              atomikosDataSourceBean.setMaxIdleTime(secondaryDataSourceProperties.getMaxIdleTime());
              atomikosDataSourceBean.setTestQuery(secondaryDataSourceProperties.getTestQuery());
              return atomikosDataSourceBean;
          }
      
          @Bean(name = "sqlSessionFactory2")
          public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource2") DataSource dataSource) throws Exception {
              SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
              bean.setDataSource(dataSource);
              bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
              org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
              configuration.setLazyLoadingEnabled(true);
              //configuration.setLogImpl(StdOutImpl.class);
              configuration.setMapUnderscoreToCamelCase(true);
              configuration.setDefaultExecutorType(ExecutorType.REUSE);
              configuration.setCacheEnabled(false);
              configuration.setDefaultStatementTimeout(5000);
              bean.setConfiguration(configuration);
              return bean.getObject();
          }
      
          @Bean(name = "sqlSessionTemplate2")
          public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory2") SqlSessionFactory sqlSessionFactory) {
              return new SqlSessionTemplate(sqlSessionFactory);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
      • 79
      • 80
      • 81
      • 82
      • 83
      • 84
      • 85
      • 86
      • 87
      • 88
      • 89
      • 90
      • 91
      • 92
      • 93
      • 94
      • 95
      • 96
      • 97
      • 98
      • 99
      • 100
      • 101
      • 102
      • 103
      • 104
      • 105
      • 106
      • 107
      • 108
      • 109
      • 110
      • 111
      • 112
      • 113
      • 114
      • 115
      • 116
      • 117
      • 118
      • 119
      • 120
      • 121
      • 122
      • 123
      • 124
      • 125
      • 126
      • 127
      • 128
      • 129
      • 130
      • 131
      • 132
      • 133
      • 134
      • 135
    5. 事务管理器配置

      @EnableTransactionManagement
      @Configuration
      public class TransactionConfiguration {
          @Bean(name = "userTransaction")
          public UserTransaction userTransaction() {
              //UserTransactionImp用于开启、提交、回滚事务
              return new UserTransactionImp();
          }
      
          @Bean(name = "atomikosTransactionManager")
          public TransactionManager atomikosTransactionManager() {
              UserTransactionManager userTransactionManager = new UserTransactionManager();
              userTransactionManager.setForceShutdown(false);
              return userTransactionManager;
          }
      
          @Bean(name = "transactionManager")
          @DependsOn({"userTransaction", "atomikosTransactionManager"})
          public PlatformTransactionManager transactionManager(UserTransaction userTransaction, TransactionManager atomikosTransactionManager) {
              return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
    6. Mapper代码

      //Primary数据源Mapper
      public interface Account0Mapper {
      
          int update(Account account);
      
          Account findByAccountId(@Param("accountId") String accountId);
      
          Account findByAmountIdForUpdate(@Param("accountId") String accountId);
      }
      //Secondary数据源Mapper
      public interface Account0Mapper {
      		//与Primary数据源Mapper方法一样
        	...省略...
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
    7. Service代码

      @Service
      public class AccountServiceImpl implements AccountService {
      
          @Autowired
          private Account0Mapper account0Mapper;
          @Autowired
          private Account1Mapper account1Mapper;
      
          @Override
          @Transactional
          public void transfer(AccountTransfer accountTransfer) {
              String accountFromId = accountTransfer.getAccountFromId();
              String accountToId = accountTransfer.getAccountToId();
              BigDecimal amount = accountTransfer.getAmount();
              //转账账户
              Account accountFromExist = account0Mapper.findByAccountId(accountFromId);
              if (accountFromExist == null) {
                  throw new RuntimeException(accountFromId + "不存在");
              }
              accountFromExist = account0Mapper.findByAmountIdForUpdate(accountFromId);
              if (accountFromExist.getAmount().subtract(amount).compareTo(BigDecimal.ZERO) < 0) {
                  throw new RuntimeException(accountFromId + "账户余额不足");
              }
              Account accountNew = new Account();
              accountNew.setId(accountFromExist.getId());
              accountNew.setAmount(accountFromExist.getAmount().subtract(amount));
              account0Mapper.update(accountNew);
      
              //接收账户
              Account accountToExist = account1Mapper.findByAccountId(accountToId);
              if (accountToExist == null) {
                  throw new RuntimeException(accountToId + "不存在");
              }
              Account accountNew1 = new Account();
              accountNew1.setId(accountToExist.getId());
              accountNew1.setAmount(accountToExist.getAmount().add(amount));
              account1Mapper.update(accountNew1);
      
              //
              if (accountTransfer.isMockException()) {
                  throw new RuntimeException("模拟出现异常");
              }
          }
      }
      
      @Setter
      @Getter
      @Builder
      @AllArgsConstructor
      @NoArgsConstructor
      public class AccountTransfer {
          /**
           * 转账账户
           */
          private String accountFromId;
          /**
           * 进账账户
           */
          private String accountToId;
          private BigDecimal amount;
          private boolean mockException;
      
      }
      
      @Setter
      @Getter
      @Builder
      @AllArgsConstructor
      @NoArgsConstructor
      public class Account implements Serializable {
          private static final long serialVersionUID = 5454155825314635342L;
          private Integer id;
          private String accountId;
          private BigDecimal amount;
          private BigDecimal freezedAmount;
          private Date createTime;
          private Date updateTime;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
    8. 启动代码

      @SpringBootApplication
      public class AccountJTAApplication {
          public static void main(String[] args) {
              new SpringApplicationBuilder(AccountJTAApplication.class)
                      .web(WebApplicationType.NONE)
                      .run(args);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    9. 单元测试代码

      @RunWith(SpringJUnit4ClassRunner.class)
      @SpringBootTest
      public class AccountJTATest {
          @Autowired
          private AccountService accountService;
      
          /**
           * 正常
           */
          @Test
          public void testTransfer() {
              AccountTransfer accountTransfer = new AccountTransfer();
              accountTransfer.setAccountFromId("jannal");
              accountTransfer.setAccountToId("tom");
              accountTransfer.setAmount(new BigDecimal(1000));
              accountService.transfer(accountTransfer);
          }
      
          /**
           * 第二个数据源业务异常
           */
          @Test
          public void testTransferException() {
              AccountTransfer accountTransfer = new AccountTransfer();
              accountTransfer.setAccountFromId("jannal");
              //不存在的账户
              accountTransfer.setAccountToId("jack");
              accountTransfer.setAmount(new BigDecimal(1000));
              accountService.transfer(accountTransfer);
          }
      
          /**
           * 第一个和第二个数据源业务都正常,方法结束前异常(模拟)
           */
          @Test
          public void testTransferMockException() {
              AccountTransfer accountTransfer = new AccountTransfer();
              accountTransfer.setAccountFromId("jannal");
              accountTransfer.setAccountToId("tom");
              accountTransfer.setAmount(new BigDecimal(1000));
              accountTransfer.setMockException(true);
              accountService.transfer(accountTransfer);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
    10. 运行日志

      # 1. 正常操作日志,可以看到commit
      ...省略...
      c.a.d.xa.XATransactionalResource         : dataSource: refreshed XAResource
      c.a.datasource.xa.XAResourceTransaction  : XAResource.start ( 3139322E3136382E3130312E382E746D313635313732373939303436353030303031:3139322E3136382E3130312E382E746D31 , XAResource.TMNOFLAGS ) on resource dataSource represented by XAResource instance com.mysql.jdbc.jdbc2.optional.JDBC4SuspendableXAConnection@20de05e5
      c.a.d.xa.XATransactionalResource         : dataSource2: refreshed XAResource
      c.a.datasource.xa.XAResourceTransaction  : XAResource.start ( 3139322E3136382E3130312E382E746D313635313732373939303436353030303031:3139322E3136382E3130312E382E746D32 , XAResource.TMNOFLAGS ) on resource dataSource2 represented by XAResource instance com.mysql.jdbc.jdbc2.optional.JDBC4SuspendableXAConnection@459b187a
      c.a.datasource.xa.XAResourceTransaction  : XAResource.end ( 3139322E3136382E3130312E382E746D313635313732373939303436353030303031:3139322E3136382E3130312E382E746D31 , XAResource.TMSUCCESS ) on resource dataSource represented by XAResource instance com.mysql.jdbc.jdbc2.optional.JDBC4SuspendableXAConnection@20de05e5
      c.a.datasource.xa.XAResourceTransaction  : XAResource.end ( 3139322E3136382E3130312E382E746D313635313732373939303436353030303031:3139322E3136382E3130312E382E746D32 , XAResource.TMSUCCESS ) on resource dataSource2 represented by XAResource instance com.mysql.jdbc.jdbc2.optional.JDBC4SuspendableXAConnection@459b187a
      c.a.datasource.xa.XAResourceTransaction  : XAResource.prepare ( 3139322E3136382E3130312E382E746D313635313732373939303436353030303031:3139322E3136382E3130312E382E746D31 ) returning OK on resource dataSource represented by XAResource instance com.mysql.jdbc.jdbc2.optional.JDBC4SuspendableXAConnection@20de05e5
      c.a.datasource.xa.XAResourceTransaction  : XAResource.prepare ( 3139322E3136382E3130312E382E746D313635313732373939303436353030303031:3139322E3136382E3130312E382E746D32 ) returning OK on resource dataSource2 represented by XAResource instance com.mysql.jdbc.jdbc2.optional.JDBC4SuspendableXAConnection@459b187a
      c.a.datasource.xa.XAResourceTransaction  : XAResource.commit ( 3139322E3136382E3130312E382E746D313635313732373939303436353030303031:3139322E3136382E3130312E382E746D31 , false ) on resource dataSource represented by XAResource instance com.mysql.jdbc.jdbc2.optional.JDBC4SuspendableXAConnection@20de05e5
      c.a.datasource.xa.XAResourceTransaction  : XAResource.commit ( 3139322E3136382E3130312E382E746D313635313732373939303436353030303031:3139322E3136382E3130312E382E746D32 , false ) on resource dataSource2 represented by XAResource instance com.mysql.jdbc.jdbc2.optional.JDBC4SuspendableXAConnection@459b187a 
      
      # 1.1 正常的事务日志
      {"id":"192.168.1.6.tm165175116472200001","wasCommitted":true,"participants":[{"uri":"192.168.1.6.tm1","state":"COMMITTING","expires":1651751175821,"resourceName":"dataSource"},{"uri":"192.168.1.6.tm2","state":"COMMITTING","expires":1651751175821,"resourceName":"dataSource2"}]}
      {"id":"192.168.1.6.tm165175116472200001","wasCommitted":true,"participants":[{"uri":"192.168.1.6.tm1","state":"TERMINATED","expires":1651751175832,"resourceName":"dataSource"},{"uri":"192.168.1.6.tm2","state":"TERMINATED","expires":1651751175832,"resourceName":"dataSource2"}]}
      
      # 2. 异常操作日志,可以看到rollback
      ...省略...
      c.a.d.xa.XATransactionalResource         : dataSource: refreshed XAResource
      c.a.datasource.xa.XAResourceTransaction  : XAResource.start ( 3139322E3136382E3130312E382E746D313635313732383132393437383030303031:3139322E3136382E3130312E382E746D31 , XAResource.TMNOFLAGS ) on resource dataSource represented by XAResource instance com.mysql.jdbc.jdbc2.optional.JDBC4SuspendableXAConnection@27bcb4ad
      c.a.datasource.xa.XAResourceTransaction  : XAResource.end ( 3139322E3136382E3130312E382E746D313635313732383132393437383030303031:3139322E3136382E3130312E382E746D31 , XAResource.TMSUCCESS ) on resource dataSource represented by XAResource instance com.mysql.jdbc.jdbc2.optional.JDBC4SuspendableXAConnection@27bcb4ad
      c.a.datasource.xa.XAResourceTransaction  : XAResource.rollback ( 3139322E3136382E3130312E382E746D313635313732383132393437383030303031:3139322E3136382E3130312E382E746D31 ) on resource dataSource represented by XAResource instance com.mysql.jdbc.jdbc2.optional.JDBC4SuspendableXAConnection@27bcb4ad
      
      # 2.1 异常的事务日志
      {"id":"192.168.1.6.tm165175128383300001","wasCommitted":false,"participants":[{"uri":"192.168.1.6.tm1","state":"TERMINATED","expires":1651751294869,"resourceName":"dataSource"},{"uri":"192.168.1.6.tm2","state":"TERMINATED","expires":1651751294869,"resourceName":"dataSource2"}]}
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
  • 相关阅读:
    vue3的宏到底是什么东西?
    跨境电商品牌如何制定成功的聊天机器人策略
    【C++11算法】minmax和minmax_element
    基础算法:排序 二分 高精度 前缀和与差分 双指针算法 位运算 离散化 区间合并
    返回引用类型的函数指针(c++)
    ZETA与RFID在供应链物流场景中是否可以互补?
    ID VS UUID 主键详解
    007.iSCSI服务器CHAP双向认证配置
    c++ 之安装opencv显示图片
    Java学习笔记3.6.1 final关键字 - final修饰类
  • 原文地址:https://blog.csdn.net/usagoole/article/details/126162545