分布式事务

什么是分布式事务

分布式事务指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。简而言之,就是将一个大的操作拆分成多个小操作,这些小的操作分布在不同的服务器上,属于不同的应用,分布式事务就需要保证这些小操作要么全部成功,要么全部失败。 本质上来说,分布式事务就是为了保证不同数据库的数据一致性。

为什么要用分布式事务

现在大部分公司的系统都是做了微服务化,在微服务架构中,被调用增删改操作的微服务无法感知其他微服务的操作结果,这可能导致数据的一致性被破坏,所以要用分布式事务来避免数据的不一致

举个栗子:跨行转账,A给B转100元,由于两个银行的接口都是独立的,需要先对A的账户冻结减去100元,冻结数目增加100元,如果转账成功,冻结数目变成0元,对方账号增加100元,如果失败。自己的账号增加冻结的100元,冻结数目变成0元

分布式事务的理论

分布式事务解决了多个微服务之间数据可能不一致的问题,一般分布式事务有个cap理论。 CAP理论说的是在一个分布式系统中,最多只能满足C、A、P中的两个需求。

CAP的含义:

C:Consistency 一致性

同一数据的多个副本是否实时相同。

A:Availability 可用性

可用性:一定时间内系统返回一个明确的结果 则称为该系统可用。

P:Partition tolerance 分区容错性

将同一服务分布在多个系统中,从而保证某一个系统宕机,仍然有其他系统提供相同的服务。

CAP理论告诉我们,在分布式系统中,C、A、P三个条件中我们最多只能选择两个。

BASE理论

在分布式系统中,我们往往追求的是可用性,它的重要程序比一致性要高,那么如何实现高可用性呢?前人已经给我们提出来了另外一个理论,就是BASE理论,它是用来对CAP定理 进行进一步扩充的。BASE理论指的是:

Basically Available(基本可用)

Soft state(软状态)

Eventually consistent(最终一致性)

BASE理论是对CAP中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。

分布式事务解决方案

接下来为大家介绍业界常见的事务处理模式,包括两阶段提交、本地消息表、MQ、TCC等

1. 两阶段提交

两阶段提交基于XA协议实现,XA协议指的是TM(事务管理器)和RM(资源管理器)之间的接口。目前主流的关系型数据库产品都是实现了XA接口的。JTA(Java Transaction API)是符合X/Open DTP模型的,事务管理器和资源管理器之间也使用了XA协议。在JavaEE平台下,WebLogic、Webshare等主流商用的应用服务器提供了JTA的实现和支持。而在Tomcat下是没有实现的,但可以借助第三方的框架Jotm、Automikos等来实现,两者均支持spring事务整合。此种方式优点是实现难度不算太高。比较适合传统的单体应用,在同一个方法中存在跨库操作的情况,缺点是性能比较差,不适合高并发和高性能的业务场景。

2. 本地消息表

本地消息表应该是目前业界使用比较多的分布式事务处理方式。其核心思想是将分布式事务拆分成本地事务进行处理,这种思路是来源于ebay。

基本思路就是:

消息生产方,需要额外建一个消息表,并记录消息发送状态。消息表和业务数据要在一个事务里提交,也就是说他们要在一个数据库里面。然后消息会经过MQ发送到消息的消费方。如果消息发送失败,会进行重试发送。

消息消费方,需要处理这个消息,并完成自己的业务逻辑。此时如果本地事务处理成功,表明已经处理成功了,如果处理失败,那么就会重试执行(业务上实现幂等)。如果是业务上面的失败,可以给生产方发送一个业务补偿消息,通知生产方进行回滚等操作。

生产方和消费方定时扫描本地消息表,把还没处理完成的消息或者失败的消息再发送一遍。如果有靠谱的自动对账补账逻辑,这种方案还是非常实用的。

这种方案遵循BASE理论,采用的是最终一致性,这种方案是比较适合实际业务场景的,即不会出现像2PC那样复杂的实现(当调用链很长的时候,2PC的可用性是非常低的),也不会像TCC那样可能出现确认或者回滚不了的情况。

优点: 一种非常经典的实现,避免了分布式事务,实现了最终一致性。

缺点: 消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理。

3. MQ事务消息

有一些第三方的MQ是支持事务消息的,比如RocketMQ,开源的版本从4.3.0开始支持事务消息,它的方式也是类似于采用的二阶段提交。但是市面上一些主流的MQ都是不支持事务消息的,比如 RabbitMQ 和 Kafka 都不支持。

其思路大致为:

第一阶段发送Half消息,执行成功会拿到消息的地址。

第二阶段执行本地事务

第三阶段通过第一阶段拿到的地址去访问消息,并修改状态,消费方才能收到该消息。

也就是说在业务方法内要对消息队列提交两次请求,一次发送消息和一次确认消息。如果确认消息发送失败了RocketMQ会定期扫描消息集群中的事务消息(目前默认是5次),这时候发现了Half消息,它会向消息发送者确认,所以生产方需要实现一TransactionListener接口,重写checkLocalTransaction方法,RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。

优点: 实现了最终一致性,不需要依赖本地数据库事务。

缺点: 主流MQ不支持,RocketMQ也对java之外的语言不支持

4. TCC

TCC 其实就是采用的补偿机制。一个完整的TCC业务由一个主服务和若干个从服务组成,主业务服务发起并完成整个业务活动,TCC模式要求主从服务提供三个接口:Try、Confirm、Cancel。

整个TCC业务分成两个阶段完成:

第一阶段:主业务服务分别调用所有从业务的try操作,并在活动管理器中登记所有从业务服务。当所有从业务服务的try操作都调用成功或者某个从业务服务的try操作失败,进入第二阶段。

第二阶段:活动管理器根据第一阶段的执行结果来执行confirm或cancel操作。如果第一阶段所有try操作都成功,则活动管理器调用所有从业务活动的confirm操作。否则调用所有从业务服务的cancel操作。

TCC模式通过避免XA两阶段提交对数据资源的长期锁定提升了性能,达到了最终一致性,但牺牲了应用灵活性,需要开发人员实现事务检查与回滚的细节,面临着花费大量精力保证应用正确性的问题。

5. GTS

全局事务服务(Global Transaction Service,简称 GTS)是一款高性能、高可靠、接入简单的分布式事务中间件,用于解决分布式环境下的数据一致性问题。

与基于消息队列与TCC补偿模式的分布式事务相比,在性能满足的情况下,GTS更好的应用灵活性与数据一致性: 灵活性:数据库应用基本实现零修改,同时,基于XA模型,可方便的支持消息队列数据库等多种RM。数据一致性:GTS 的缺省事务隔离级别为读未提交,该模式下可以达到分布式事务的最大性能,但可能会读到脏数据。对于一致性要求高的应用,在性能允许的情况下,可以采用已提交读语句(for update、lock in share mode)将隔离级别提升至读已提交。目前GTS并未开源,由阿里云提供服务

最好的方式是不需要处理分布式事务,有些场景是只需要保证最终一致性的,我们在选择具体的方案时要具体问题具体分析,是要实时一致还是最终一致,是要高性能还是高可用,一定要做好评估。

参考文献: https://help.aliyun.com/product/48444.html?spm=a2c4g.11186623.6.540.154447a65zFxTF

https://github.com/yu199195/hmily

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章