当前位置: 首页 > 科技观察

@Transactional也能解决分布式事务?_0

时间:2023-03-22 11:51:53 科技观察

前天有个朋友问我一个问题。大致内容如下:这个读者是什么意思?小结:在Sharding-JDBC中,简单的使用了本地事务注解@Transactional,但是为什么跨库插入数据时可以同时回滚呢?我们知道在单个数据节点的情况下维护事务是非常简单的。只需要使用本地事务就可以轻松解决,比如常用的注解:@Transactional。但是分库后会有跨库事务。这个时候,本地交易还能保证交易吗?本文通过球友的提问,聊一聊Sharding-JDBC中的本地事务。本地事务Sharding-JDBC中的本地事务可能会给你一个误解。下面以product表为例:product表根据productID横向分为两个数据库,如下:分库的配置这里不赘述,详见源码。这时候批量往里面插入数据,伪代码如下:@TransactionalpublicintinsertBatch(){for(inti=0;i<10;i++){insert(product);......}}上例中使用@Transactional开启本地事务,但是在内部插入数据时,Sharding-JDB会根据product_id的shardingkey分库,所以这个业务方法必须跨越DB1和DB2这两个库,@Transactional注解可以解决吗?错觉:内部手动模拟抛出异常,果然是回滚。这个时候,很多人都懵了。Sharding-JDBC中的本地事务真的能保证分布式事务吗?真实结论:Sharding-JDBC中的本地事务不能保证分布式事务。Sharding-JDBC中的本地事务在以下两种情况下是完全支持的:支持非跨库事务,比如只分表,单库操作。支持逻辑异常引起的跨库事务,比如上面的操作,跨两个数据库插入数据,插入完成后抛出异常。本地事务不支持的情况:不支持网络和硬件异常导致的跨库事务;例如:在同一个事务中,如果更新了两个数据库,更新完成后提交前,第一个数据库down了两个库的数据提交。很好理解,不能支持网络和硬件异常导致的跨库事务。在分布式事务中,无论是两阶段提交还是三阶段提交,都直接或间接满足以下两个条件:支持为什么可以支持逻辑异常引起的跨库事务?每个人都知道Spring的本地事务并经常使用它们。不支持跨库事务,为什么Sharding-JDBC支持呢?想要看懂其中的窍门,就必须从Sharding-JDBC的源码入手。下图是Sharding-JDBC中的SQL处理流程:Sharding-JDBC中的一条SQL会被改写并拆分成不同数据源的SQL,比如一条select语句会根据shard拆分成数据源对应的SQLkey,然后在不同的数据源中执行,最后提交或者回滚。要解释上面的问题,只需要看ShardingConnection,它是Sharding-JDBC的自定义实现。继承关系如下图所示:可以看到ShardingConnection继承了java.sql.Connection,所以这个类就不用解释了。应该和JDBC有一些接触吧,一个直接和数据库打交道的类。想知道为什么支持跨库事务回滚,就要找到回滚的方法,如下:@Overridepublicvoidrollback()throwsSQLException{//①Localtransactionf(TransactionType.LOCAL==transactionType){超级回滚();}else{//②非本地事务shardingTransactionManager.rollback();}}rollback方法区分本地事务和分布式事务。如果是本地事务,会调用父类的rollback方法,如下://parentClass:AbstractConnectionAdapter#rollback@Overridepublicvoidrollback()throwsSQLException{//数据源存放在cachedConnections中,这里是ds1/ds2forceExecuteTemplate.execute(cachedConnections.values(),Connection::rollback);}这里是调用ForceExecuteTemplate#execute()方法执行,其实是在内部遍历数据源执行对应的rollback方法,如如下:publicvoidexecute(finalCollectiontargets,finalForceExecuteCallbackcallback)throwsSQLException{Collectionexceptions=newLinkedList<>();for(Teach:targets){try{回调。执行(每个);}catch(finalSQLExceptionex){异常。添加(前);}}throwSQLExceptionIfNecessary(异常);}这里说的很清楚了,rollback在每个数据源回滚,不记录任何事务日志,所以在非硬件和网络的情况下可以正常回滚,一旦由于网络和硬件故障,回滚某个数据源的可能会失败,这样即使程序恢复正常,也没有undolog继续回滚,所以这里的数据不一致。)不能保证跨数据库的分布式事务,所以不要被Sharding-JDBC的错觉所迷惑。当然,Sharding-JDBC对跨库事务也有一定的支持,大致可以分为三类:强一致性XA协议事务、Base-based灵活事务、通过SPI机制定制扩展的分布式事务方案。本文只是简单介绍。关于分库分表后的事务处理,后面会针对以上三类方案进行详细介绍。