当前位置: 首页 > news >正文

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

前天陈某知识星球中的朋友咨询过我一个问题,大致内容如下:

 

这位读者什么意思呢?简单的总结下:在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中的本地事务在以下两种情况是完全支持的

  1. 支持非跨库事务,比如仅分表、在单库中操作
  2. 支持因逻辑异常导致的跨库事务,比如上述的操作,跨两个库插入数据,插入完成后抛出异常

本地事务不支持的情况

  1. 不支持因网络硬件异常导致的跨库事务;例如:同一事务中,跨两个库更新,更新完毕后、未提交之前,第一个库宕机,则只有第二个库数据提交

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

  1. 有一个事务协调者
  2. 事务日志记录

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

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

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对于跨库事务也是有一定的支持,大致分成三类:

  • 强一致性的XA协议事务
  • 基于Base的柔性事务
  • 通过SPI机制自定义扩展的分布式事务解决方案

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

相关文章:

  • 快速预览工具——Wlx2Explore入门
  • 好用的在线思维导图软件--GitMind
  • 金仓数据库 KingbaseES 插件参考手册 S (1)
  • CF603E Pastoral Oddities
  • LinkedHashMap源码及LRU实现原理
  • 计算机毕业设计 SSM家具定制管理系统 家具生产管理系统 家具订单管理系统
  • nodejs安装及环境配置详细教程
  • 联想java笔试题20190618
  • Essential Macleod中的吸收工具
  • 测试环境不稳定复杂的必然性及其对策
  • zookeeper集群
  • .NET delegate 委托 、 Event 事件
  • 融云超级群的「同城社交平台」应用实践
  • Java并发进阶之:关于计算机的一些知识
  • 电气滑环更换原因分析
  • Android 初级面试者拾遗(前台界面篇)之 Activity 和 Fragment
  • Docker 笔记(1):介绍、镜像、容器及其基本操作
  • eclipse(luna)创建web工程
  • electron原来这么简单----打包你的react、VUE桌面应用程序
  • Java面向对象及其三大特征
  • LeetCode29.两数相除 JavaScript
  • SQL 难点解决:记录的引用
  • Swift 中的尾递归和蹦床
  • TCP拥塞控制
  • TypeScript迭代器
  • 安装python包到指定虚拟环境
  • 大整数乘法-表格法
  • 第2章 网络文档
  • 基于Volley网络库实现加载多种网络图片(包括GIF动态图片、圆形图片、普通图片)...
  • 漂亮刷新控件-iOS
  • 前端
  • 使用parted解决大于2T的磁盘分区
  • 通过npm或yarn自动生成vue组件
  • 用jQuery怎么做到前后端分离
  • 优秀架构师必须掌握的架构思维
  • 在Mac OS X上安装 Ruby运行环境
  • Spark2.4.0源码分析之WorldCount 默认shuffling并行度为200(九) ...
  • 正则表达式-基础知识Review
  • !$boo在php中什么意思,php前戏
  • #Java第九次作业--输入输出流和文件操作
  • $.extend({},旧的,新的);合并对象,后面的覆盖前面的
  • (02)Hive SQL编译成MapReduce任务的过程
  • (12)Linux 常见的三种进程状态
  • (NO.00004)iOS实现打砖块游戏(十二):伸缩自如,我是如意金箍棒(上)!
  • (zt)最盛行的警世狂言(爆笑)
  • (二)PySpark3:SparkSQL编程
  • (更新)A股上市公司华证ESG评级得分稳健性校验ESG得分年均值中位数(2009-2023年.12)
  • (一)基于IDEA的JAVA基础12
  • (一一四)第九章编程练习
  • (转载)Google Chrome调试JS
  • .NET Standard 的管理策略
  • .NET 使用 XPath 来读写 XML 文件
  • .NET 自定义中间件 判断是否存在 AllowAnonymousAttribute 特性 来判断是否需要身份验证
  • .NET开源的一个小而快并且功能强大的 Windows 动态桌面软件 - DreamScene2
  • .net连接MySQL的方法