传统XA模式
XA 是数据库厂商实现的两阶段提交的一个强一致的协议(也就是对 2PC 规范的落地)
换句话说,XA 模式也就是基于事务资源(数据库)本身提供的 XA 规范和协议的支持
它的核心价值:
- 从场景上看,满足全局一致性的需求
- 从应用上看,保持与 AT 模式一样的无侵入
- 从机制上看,适应分布式微服务架构的特点
XA 模式的优劣势:
- 优势:
- 业务无侵入(无需业务逻辑参与)
- 数据库支持广泛(XA 协议被主流关系型数据库广泛支持,无需额外的适配即可使用)
- 多语言支持容易(对于 Seata 来说,改造时,其对 RM 要求较少,所以为不同语言开发 SDK 更容易)
- 传统基于 XA 事务的应用可以平滑的迁移到 Seata 平台
- 劣势:
- 性能差
- 数据锁定:数据在整个事务处理过程结束前,都被锁定,读写都按隔离级别的定义约束起来
- 协议阻塞:XA prepare 后,分支事务进入阻塞阶段,收到 XA commit 或 XA rollback 前必须阻塞等待
关于性能,主要来自两方面:
- 事务协调过程增加单个事务 RT(RequestTime请求耗时),降低吞吐(时间都耗在等待全局事务处理完,整个请求才完)
- 并发事务数据的锁冲突
可能有人觉得我锁的都是我的记录,其它记录根本不影响别人用啊,我也不觉得它的性能衰减能有多严重啊
这虽然也有些道理,但要注意:在锁的过程中,数据库连接还没释放,还占用连接池里的一个连接
如果多来几个分布式事务,连接池里的连接都用光了,导致可能有的事务等的不是你的锁,等的是数据库连接
尽管如此,它的一致性保障还是非常好的,如果真正有必要用的话,也可以用
比如短事务:就一两个子事务的情况,可以马上把事务对齐,马上提交,那也 OK
SeataXA模式
该模式的前提是:需要支持 XA 事务的数据库,并且 Java 应用通过 JDBC 访问数据库
它分成以下两个阶段
- 执行阶段(Execute):XA start/XA end/XA prepare + 注册分支
- 完成阶段(Finish):XA commit/XA rollback
处理流程就非常简单了:
- TM 向 TC 开启全局事务
- 每个分支事务注册到 TC
- 各个 RM 中的分支事务自己去处理(执行SQL),也不用向 TC 上报事务状态了(因为分支事务根本就没提交)
- 最后 TC 再统一调用 commit 或 rollback 就行了
数据源代理
XA 模式 XAConnection,一般有两种方案:
- 要求开发者配置XADataSource(这时开发人员就需要显式的知道:系统接入 XA,并适当的去了解相关知识)
- 根据开发者的普通DataSource来创建(这时开发人员用的还是普通的DataSourcePool,做到了业务完全无感知)
第二种方案的好处是:开发者完全不必关心 XA 层面的任何问题,保持本地编程模型即可
只不过它也有局限:无法保证兼容的正确性
因为它是把原来的 connection 替换成了 XAConnection,相当于是自己重写了连接,自己去实现了 XA 驱动
其本质是在做数据库驱动程序要做的事情,等于是为数据库驱动程序补充功能
而不同厂商不同版本的数据库驱动实现机制是厂商私有的,开发者使用的驱动程序版本差异很可能造成机制的失效
这点在 Oracle 上体现非常明显。参见 Druid issue:https://github.com/alibaba/druid/issues/3707
并且第一种方案基于 XA 数据源进行代理的方式:其实业务上的改动并不大,是可以接受的
Seata高可用
TC有状态部署
如果把 TC 中的数据存到本地文件,那 TC 就变成有状态部署了。但若 TC 挂了,然后在另一台机器再部署一个 TC
那新 TC 是没有数据的,即无法异地恢复,除非把原 TC 的存储文件拿到新 TC 机器上,但如果原 TC 机器也挂了呢
这就是有状态部署的问题:不支持异地恢复(像 ShardingSphere 的分布式事务解决方案,它就不支持异地恢复)
TC无状态部署
TC 数据都放到一个公共存储里(比如数据库),这样TC就是无状态的了
就可以无限扩展 TC(分布式玩的就是扩展性),每次扩展后都会注册到注册中心,这就保证了高可用
一致性协议同步
还有一种方案,是把数据放到 TC 内部,但这就涉及到数据同步(考虑数据一致性协议,处理数据分块和数据路由等)
它的优点是可以存储非常多的数据(貌似是用 RocksDB 来存的),理论上是无限量的数据(支持不停的扩机器)
这里就略了:毕竟本身 TC 就没有多少数据,用不着搞这么麻烦,真到了业务量这么大的那天,估计做梦都乐醒了吧