前天有个朋友问我一个问题。大致内容如下:这个读者是什么意思?小结:在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(finalCollection
