分布式事务

一、分布式理论

单个数据库的性能产生瓶颈的时候,我们可能对数据库进行分区,这里所说的分区是指物理分区,分区之后可能不同的库就处于不同的服务器上了,这个时候单个数据库的ACID已经不能适应这种情况了,而在这种ACID的集群环境下,再想保证集群的ACID会导致我们的系统变得很差,这个时候我们需要引入CAP原则。

一致性(Consistency) : 客户端知道一系列的操作都会同时发生(生效)

可用性(Availability) : 每个操作都必须以可预期的响应结束

分区容错性(Partition tolerance) : 即使出现单个组件无法可用,操作依然可以完成

具体地讲在分布式系统中,在任何数据库设计中,一个Web应用至多只能同时支持上面的两个属性。显然,任何横向扩展策略都要依赖于数据分区。因此,设计人员必须在一致性与可用性之间做出选择。

[了解]

① 2PC的概念

数据库支持2PC(两阶段提交),又叫XA Transactions,MySQL5.5SQL Server2005Oracle7开始支持。

其中,XA是一个两阶段提交协议,该协议分为以下两个阶段:

1、第一阶段:事务协调器要求每个涉及到事务的数据库预提交(precommit)此操作,并反映是否可以提交。

2、第二阶段:事务协调器要求每个数据库提交数据。

如果任何一个数据库否决此次提交,那么所有数据库都会被要求回滚它们在次数据库中的那部分信息。这里满足了一致性,那么可用性将会受到影响。

② base理论

在分布式系统中,我们往往追求的是可用性,它的重要程度比一致性要高,那么如何实现高可用性呢?那就是BASE理论,它用来对CAP定理进一步的扩充:

  • Basically Available(基本可用)

  • Soft state(软状态)

  • Eventually consistent(最终一致性)

二、分布式事务解决方案

①两阶段提交(2PC)

见上,就是有一个预提交的过程,两阶段提交就是使用XA协议的原理

优点: 尽量保证了数据的强一致,适合对数据强一致要求很高的关键领域。(其实也不能100%保证强一致)

缺点: 实现复杂,牺牲了可用性,对性能影响较大,不适合高并发高性能场景。

②补偿事务(TCC)

其核心思想是:针对每一个操作,都要注册一个与其对应的确认和补偿(撤销)操作,它分为三个阶段:

· Try阶段主要是对业务系统做检测及资源预留。

· Confirm阶段主要是对业务系统做确认提交,Try阶段执行成功并开始执行Confirm阶段时,默认Confirm阶段时不会出错的。即Try成功,Confirm一定成功。

· Cancel阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。

【示例】

假如Bob要向Smith转账,思路大概是:我们有一个本地方法,里面依次调用

1.首先在Try阶段,要先调用远程接口把SmithBob的钱给冻结起来。 2.在Confirm阶段,执行远程调用的转账的操作,转账成功进行解冻。

如何第2不步执行成功,那么转账成功,如果第二部执行失败,则调用远程冻结接口对应的解冻方法。

优点:2PC比起来,实现以及流程相对简单了一些,但数据的一致性比2PC也要差一些

缺点: 缺点还是比较明显的,在2,3步中都有可能失败。TCC属于应用层的一种补偿方式,所以需要程序员在实现的时候多写很多补偿的代码,在一些场景中,一些业务流程可能用TCC不太好定义及处理。

③本地消息表(异步确保)

本地消息表这种实现方式应该是业界使用最多的,其核心思想是将分布式事务拆分成本地事务进行处理,这种思想是来源于ebay。我们可以从下面的流程图中看出其中的一些细节:

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

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

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

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

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

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

MQ事务消息

有一些第三方的MQ是支持事务消息的,比如RocketMQ,他们支持事务消息的方式也是类似于采用的二阶段提交,但市面上一些主流MQ不支持事务消息的,如RabbitMQKafka

如阿里的RocketMQ中间件为例,其思路大致为:

第一阶段Prepared消息,会拿到消息的地址。

第二阶段执行本地事务,第三阶段通过第一阶段拿到的地址去访问消息,并修改状态。

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

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

缺点: 实现难度大,主流MQ不支持。

posted @ 2019-09-17 19:50  loading---  阅读(142)  评论(0编辑  收藏  举报