分布式-分布式事务

一、分布式事务- 二阶段协议

1.1 前言

在单个数据库实例时候,我们可以在一个数据源的事务(本地事务)内做多步数据库操作,在事务内的多个操作要么全部执行生效,要么全部不生效。在多数据实例节点时候,我们对多个实例的数据源进行操作时候就没办法把多个操作放到一个大的事务内来保证原子性了,因为多个实例操作的是不同的数据源,而数据库自带的事务是针对单个数据源来说的。

1.2 二阶段协议

单个数据库实例内的事务我们称为本地事务,需要保证多个数据源的操作要么都做要么都不做的事务我们成为分布式事务,为了实现分布式事务,二阶段与三阶段协议就应运而生了。

分布式事务由事务发起者、资源管理器(参与者)、事务协调者组成,下面我们看看二阶段协议内容:

1.2.1 第一阶段

分布式事务发起方向事务协调器发起分布式事务,协调器则向所有事务参与者发起准备请求,事务参与者接受到请求后执行本地事务,但是不提交。如果所有事务参与者都返回了准备OK到事务协调器

则事务协调器准备进入第二阶段。如果有一个参与者返回准备失败,则事务协调器向所有参与者发起事务回滚请求,事务参与者收到请求后回滚执行的本地事务,则分布式事务结束。

1.2.2 第二阶段

事务协调器向所有事务参与者发起提交事务的请求,事务参与者接受到请求后,执行本地事务的提交操作。如果事务协调器收到所有参与者提交OK则分布式事务结束。

1.3 总结

二阶段协议是个标准协议,协议只是规定了分布式事务实现的骨架,并没有规定具体如何实现,比如事务协调器是作为一个单独应用存在,还是与事务发起方一起部署的?事务参与者是单独的应用还是与发起方一起的?并且并没有考虑异常情况,比如第二阶段如果有部分参与者返回提交失败或者由于网络原因返回了提交OK,但是事务协调器没有收到,该怎么处理?

另外分布式事务并不是简单说对多个数据源操作的原子性,还可以是数据源操作与远程RPC的原子性,数据源与发送消息的原子性。

二、分布式事务- 三阶段协议

2.1 前言

前面我们介绍了为解决分布式事务而提出来的的二阶段协议,本文首先来讲解二阶段的不足,然后阐述三阶段协议,三阶段协议也是一个标准的协议,也并没有说具体如何实现。

2.2 二阶段协议存在的问题

  • 主要是同步阻塞问题
    在二阶段的第一阶段所有参与者接受到事务协调器的事务准备请求后,会在本地开启并执行事务,但是没有提交事务。所有参与者等待第二阶段事务协调器发出事务提交或者回滚后才会提交或者回滚事务。而在这期间所有参与者开启的本地事务一直存在,也就是一直把相应的资源锁定了(比如本地事务要更新一行数据,则在开启事务后,事务提交或者回滚之前都一直通过行锁锁定了这行数据),导致其他需要访问这行数据的事务阻塞等待。

假如在第一阶段事务协调器给10个参与者发送准备请求,其中9个参与者正确接受了,并开启了本地事务锁定了具体的资源,而剩下一个参与者或者由于网络问题没有收到准备请求,或者接受到了但是本事事务执行失败,或者执行正常,但是给事务协调器的回执由于网络原因没有被协调器收到等,则事务协调器发现其中一个参与者返回准备失败或者等待超时后还没收到那一个参与者的回执则会通知所有的参与者执行回滚操作。也就是在具体回滚前,其他9个参与者白白的锁定了本地资源,成功的阻止了在开启事务和回滚之前其他事务访问锁定的资源,这显然很浪费。

2.3 三阶段协议

三阶段协议把二阶段的第一阶段在细分为2阶段,具体内容如下:

2.3.1 第一阶段canCommit

事务发起方发起事务后,事务协调器会给所有的事务参与者发起canCommit?的请求,参与者收到后根据自己的情况判断是否可以执行提交,如果可以则回执OK,否者返回fail,并不开启本地事务并执行。具体参与者是如何判断本地是否可以执行提交协议并没有具体规定,需要协议实现者自己规定,比如可能判断参与者是否存在(网络是否OK)或者本地数据库连接是否可用来判断(YY)。
如果协调器发现有些发起方返回fail或者等待超时后参与者还没返回则给所有事务参与者发起中断操作,具体中断操作做什么协议也没有具体规定。如果协调器发现所有参与者返回可以提交,则进入第二阶段。

2.3.1 第二阶段preCommit

事务协调器向所有参与者发起准备事务请求,参与者接受到后,开启本地事务并执行,但是不提交。剩下的与二阶段的一阶段一致。

2.3.1 第三阶段doCommit

与二阶段中的二阶段一致。

2.4 总结

三阶段与二阶段最大不同在于三阶段协议把二阶段的第一阶段拆分为了两个阶段,其中第一阶段并不锁定资源,而是询问参与者是否可以提交,等所有参与者回复OK后在具体执行第二阶段锁定资源。理论上如果第一阶段返回都OK,则第二阶段和三阶段执行成功的概率就很大,另外如果第一阶段有些参与者返回了fail,由于这时候其他参与者还没有锁定资源,所以不会造成资源的阻塞。

三、分布式事务- TCC编程式模式

3.1 前言

严格遵守ACID的分布式事务我们称为刚性事务,而遵循BASE理论(基本可用:在故障出现时保证核心功能可用,软状态:允许中间状态出现,最终一致性:不要求分布式事务打成中时间点数据都是一致性的,但是保证达到某个时间点后,数据就处于了一致性了)的事务我们称为柔性事务,其中TCC编程模式就属于柔性事务,本文我们来阐述其理论。

3.2 TCC编程模式

  • TCC编程模式本质上也是一种二阶段协议,不同在于TCC编程模式需要与具体业务耦合

3.3 TCC编程模式步骤:

  • 所有事务参与方都需要实现try,confirm,cancle接口。

  • 事务发起方向事务协调器发起事务请求,事务协调器调用所有事务参与者的try方法完成资源的预留,这时候并没有真正执行业务,而是为后面具体要执行的业务预留资源,这里完成了一阶段。
    如果事务协调器发现有参与者的try方法预留资源时候发现资源不够,则调用参与方的cancle方法回滚预留的资源,需要注意cancle方法需要实现业务幂等,因为有可能调用失败(比如网络原因参与者接受到了请求,但是由于网络原因事务协调器没有接受到回执)会重试。

  • 如果事务协调器发现所有参与者的try方法返回都OK,则事务协调器调用所有参与者的confirm方法,不做资源检查,直接进行

3.4 具体的业务操作。

  • 如果协调器发现所有参与者的confirm方法都OK了,则分布式事务结束。

  • 如果协调器发现有些参与者的confirm方法失败了,或者由于网络原因没有收到回执,则协调器会进行重试。这里如果重试一定次数后还是失败,会怎么样那?常见的是做事务补偿。

  • 蚂蚁金服基于TCC实现了XTS(云上叫DTS),目前在蚂蚁金服云上有对外输出,这里我们来结合其提供的一个例子来具体理解TCC的含义,以下引入蚂蚁金服云实例:

“首先我们假想这样一种场景:转账服务,从银行 A 某个账户转 100 元钱到银行 B 的某个账户,银行 A 和银行 B 可以认为是两个单独的系统,也就是两套单独的数据库。

我们将账户系统简化成只有账户和余额 2 个字段,并且为了适应 DTS 的两阶段设计要求,业务上又增加了一个冻结金额(冻结金额是指在一笔转账期间,在一阶段的时候使用该字段临时存储转账金额,该转账额度不能被使用,只有等这笔分布式事务全部提交成功时,才会真正的计入可用余额)。按这样的设计,用户的可用余额等于账户余额减去冻结金额。这点是理解参与者设计的关键,也是 DTS 保证最终一致的业务约束。”

在try阶段并没有对银行A和B数据库中的余额字段做操作,而是对冻结金额做的操作,对应A银行预留资源操作是对冻结金额加上100元,这时候A银行账号上可用钱为余额字段-冻结金额;对应B银行的操作是对冻结金额上减去100,这时候B银行账号上可用的钱为余额字段-冻结金额。

如果事务协调器调用银行A和银行B的try方法有一个失败了(比如银行A的账户余额不够了),则调用cancle进行回滚操作(具体是对冻结金额做反向操作)。如果调用try方法都OK了,则进入confirm阶段,confirm阶段则不做资源检查,直接做业务操作,对应银行A要在账户余额减去100,然后冻金额减去100;对应银行B要对账户余额字段加上100,然后冻结金额加上100。

最关心的,如果confirm阶段如果有一个参与者失败了,该如何处理,其实上面操作都是xts-client做的,还有一个xts-server专门做事务补偿的。

3.5 总结

TCC是对二阶段的一个改进,try阶段通过预留资源的方式避免了同步阻塞资源的情况,但是TCC编程需要业务自己实现try,confirm,cancle方法,对业务入侵太大,实现起来也比较复杂。

3.6 应用场景

3.7 其他分布式事务处理策略

3.7.1 可靠消息最终一致性

3.7.2 最大努力通知型

四、CAP

  • CAP理论是由EricBrewer教授提出的,在设计和部署分布式应用的时候,存在三个核心的系统需求

4.1 C(Consistency)

  • 一致性是说数据的原子性,这种原子性在经典ACID的数据库中是通过事务来保证的,当事务完成时,无论其是成功还是回滚,数据都会处于一致的状态.
  • 在分布式环境中,一致性是说多点的数据是否一致.

4.2 A(Availability)

  • 可用性是说服务能一直保证是可用的状态,当用户发出一个请求,服务能在有限时间内返回结果。

  • 而这种可用性是不关乎结果的正确与否,所以,如果服务一致返回错误的数据,其实也可以称为其是可用的。

4.3 P(Tolerance of network Partition)

  • 是指网络的分区
  • 网络中的两个服务结点出现分区的原因很多,比如网络断了、对方结点因为程序bug或死机等原因不能访问

4.4 CAP

  • 一个分布式系统不可能满足一致性,可用性和分区容错性这三个需求,最多只能同时满足两个。

原始证明:
Brewer’s CAP Theorem
http://www.julianbrowne.com/article/brewers-cap-theorem

我们也来推演证明一下?

  • CA 传统数据库
  • AP 大多数网站架构的选择
  • CP Redis MongoDB

用户对数据的不一致性是不敏感的(数字敏感的场景除外)

4.5 BASE

BASE是AP组合的延伸

  • Basically Availble –基本可用
  • Soft-state –软状态/软事务
  • Eventual Consistency –最终一致性

  • BASE模型牺牲高一致性,获得可用性或可靠性: Basically Available基本可用

  • Soft state软状态 状态可以有一段时间不同步,异步。
  • Eventually consistent最终一致,最终数据是一致的就可以了,而不是时时一致。

BASE思想主要强调基本的可用性,如果你需要高可用性,也就是纯粹的高性能,那么就要以一致性或容错性为牺牲,BASE思想的方案在性能上还是有潜力可挖的
ebay架构分享:http://www.infoq.com/cn/news/2008/03/ebaybase

五、Eventual Consistency(最终一致性)

Amazon popularized the concept of “Eventual Consistency”. Their definition is: The storage system guarantees that if no new updates are made to the object, eventually all accesses will return the last updated value.

5.1 典型场景

  • DNS
  • Single Write/multi read Mastaer/Slave
  • Web Cache

5.2 强一致性(即时一致性)

  • 假如A先写入了一个值到存储系统,存储系统保证后续A,B,C的读取操作都将返回最新值

5.3 弱一致性

  • 假如A先写入了一个值到存储系统,存储系统不能保证后续A,B,C的读取操作能读取到最新值。此种情况下有一个“不一致性窗口”的概念,它特指从A写入值,到后续操作A,B,C读取到最新值这一段时间。

5.4 最终一致性

  • 最终一致性是弱一致性的一种特例。假如A首先write了一个值到存储系统,存储系统保证如果在A,B,C后续读取之前没有其它写操作更新同样的值的话,最终所有的读取操作都会读取到最A写入的最新值。

  • 如果没有失败发生的话,“不一致性窗口”的大小依赖于以下的几个因素:

    • 交互延迟
    • 系统的负载
    • 以及复制技术中Replicate的个数(这个可以理解为master/salve模式中,salve的个数)

猜你喜欢

转载自blog.csdn.net/hardworking0323/article/details/81156935