文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2024-12-13 21:58

关注

这位读者什么意思呢?简单的总结下:在Sharding-JDBC中明明只是简单的使用@Transactional这个本地事务注解,为什么在跨库插入数据时候却能够同时回滚?

我们知道单数据节点的情况下保持事务是非常简单的,只需要使用本地事务即可轻松解决,比如常用的注解:@Transactional。

但是在分库后将会存在跨库的事务,此时本地事务还能保证事务吗?

这篇文章就以球友的提问来聊一下Sharding-JDBC中的本地事务。

本地事务

Sharding-JDBC中的本地事务可能会让大家有一个误解,还是以商品表为例:将商品表根据商品ID进行水平分库,分为两个库,如下:

分库的配置这里就不贴了,详情看源码。

此时向其中批量插入数据,伪代码如下:

@Transactional
public int insertBatch(){
for(int i=0;i<10;i++){
insert(product);
.......
}
}

上述案例中使用了@Transactional​开启了本地事务,但是内部在插入数据时,Sharding-JDB会根据product_id​这个分片键进行分库,那么这个业务方法肯定是跨了DB1、DB2​这两个库,@Transactional这个注解能解决吗?

假象:手动在内部模拟抛出异常,还真的是都rollback了。

此时很多人都迷糊了,Sharding-JDBC中的本地事务真的是可以保证分布式事务?

真实结论:Sharding-JDBC中的本地事务无法保证分布式事。

Sharding-JDBC中的本地事务在以下两种情况是完全支持的:

本地事务不支持的情况:

对于因网络、硬件异常导致的跨库事务无法支持很好理解,在分布式事务中无论是两阶段还是三阶段提交都是直接或者间接满足以下两个条件:

本地事务并未满足上述条件,自然是无法支持

为什么逻辑异常导致的跨库事务能够支持?

Spring的本地事务大家都很了解,也经常用,并不支持的跨库事务,那么为什么Sharding-JDBC中却能支持呢?

想要了解其中的猫腻必然需要从Sharding-JDBC的源码入手,下图是在Sharding-JDBC一条SQL处理的流程:

Sharding-JDBC中的一条SQL会经过改写,拆分成不同数据源的SQL,比如一条select语句,会按照其中分片键拆分成对应数据源的SQL,然后在不同数据源中的执行,最终会提交或者回滚。

想要解释上述的问题,只需要看ShardingConnection,这是Sharding-JDBC自定义实现的,继承关系如下图:

可以看到ShardingConnection​继承了java.sql.Connection,这个类就不必多解释了,在学习JDBC的时候应该都有所接触,直接和数据库打交道的一个类。

想要知道为什么支持跨库事务的回滚,肯定要找到其中的rollback方法,如下:

@Override
public void rollback() throws SQLException {
//① 本地事务
f (TransactionType.LOCAL == transactionType) {
super.rollback();
} else {
//② 非本地事务
shardingTransactionManager.rollback();
}
}

rollback​的方法中区分了本地事务和分布式事务,如果是本地事务将调用父类的rollback方法,如下:

//父类:AbstractConnectionAdapter#rollback

@Override
public void rollback() throws SQLException {
//cachedConnections中存储了数据源,这里是ds1/ds2
forceExecuteTemplate.execute(cachedConnections.values(), Connection::rollback);
}

这里是调用ForceExecuteTemplate#execute()​方法执行,其实内部就是遍历数据源去执行对应的rollback方法,如下:

public void execute(final Collection<T> targets, final ForceExecuteCallback<T> callback) throws SQLException {
Collection<SQLException> exceptions = new LinkedList<>();
for (T each : targets) {
try {
callback.execute(each);
} catch (final SQLException ex) {
exceptions.add(ex);
}
}
throwSQLExceptionIfNecessary(exceptions);
}

看到这里已经很明了了,rollback​ 在各个数据源中回滚且未记录任何事务日志,因此在非硬件、网络的情况下都是可以正常回滚的,一旦因为网络、硬件故障,可能导致某个数据源rollback失败,这样即使程序恢复了正常,也无undo日志继续进行rollback,因此这里就造成了数据不一致了。

总结

仅仅依靠Spring自带的本地事务(@Transactional)是无法保证跨库的分布式事务,不要被Sharding-JDBC的假象迷惑了。

当然Sharding-JDBC对于跨库事务也是有一定的支持,大致分成三类:

本文只是抛砖引玉简单的介绍下分库分表后的事务处理,后文会针对以上三类方案详细介绍一下。

来源:码猿技术专栏内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯