1、分布式全局id
3、分布式事务一致性
5、理解分布式事务
分布式事务
一、事务定义
简单地说,事务提供一种“要么什么都不做,要么做全套(All or Nothing)”机制。
二、数据库本地事务
1、数据库事务的ACID属性
简单而言,ACID是从不同维度描述事务的特性:
- 原子性 —— 事务操作的整体性
- 一致性 —— 事务操作下数据的正确性
- 隔离性 —— 事务并发操作下数据的正确性
- 持久性 —— 事务对数据修改的可靠性
一个支持事务(Transaction)的数据库,需要具有这4种特性,
2、InnoDB实现原理
-
事务的ACID是通过InnoDB日志和锁来保证。
-
事务的隔离性是通过数据库锁的机制实现的,
-
持久性通过redo log(重做日志)来实现,
-
原子性和一致性通过Undo log来实现。
UndoLog的原理很简单,为了满足事务的原子性,在操作任何数据之前,首先将数据备份到一个地方(这个存储数据备份的地方称为UndoLog)。然后进行数据的修改。如果出现了错误或者用户执行了ROLLBACK语句,系统可以利用Undo Log中的备份将数据恢复到事务开始之前的状态。 和Undo Log相反,RedoLog记录的是新数据的备份。在事务提交前,只要将RedoLog持久化即可,不需要将数据持久化。当系统崩溃时,虽然数据没有持久化,但是RedoLog已经持久化。系统可以根据RedoLog的内容,将所有数据恢复到最新的状态。
三、分布式事务理论
本质上来说,分布式事务就是为了保证不同数据库的数据一致性。
1、CAP
CAP 定理又被称作布鲁尔定理,是分布式计算领域公认的一个定理。在一个分布式系统中,当涉及读写操作时,只能保证一致性、可用性、分区容错性 三者中的两个,另外一个必须被牺牲。则CAP为:
- Consistency(一致性), 数据一致更新,所有数据变动都是同步的
对某个指定的客户端来说,读操作能返回最新的写操作。
- Availability(可用性), 好的响应性能
非故障的节点在合理的时间内返回合理的响应(不是错误和超时的响应)。
- Partition tolerance(分区容错性), 可靠性
当出现网络分区后,系统能够继续工作。打个比方,这里个集群有多台机器,有台机器网络出现了问题,但是这个集群仍然可以正常工作。
虽然 CAP 理论定义是三个要素中只能取两个,但放到分布式环境下来思考,我们会发现必须选择 P(分区容忍)要素,因为网络本身无法做到 100% 可靠,有可能出故障,所以分区是一个必然的现象。
因此,分布式系统理论上不可能选择 CA (一致性 + 可用性)架构,只能选择 CP(一致性 + 分区容忍性) 或者 AP (可用性 + 分区容忍性)架构,在一致性和可用性做折中选择。
另外,只能选择CP或者AP是指系统发生分区现象时无法同时保证C(一致性)和A(可用性),但不是意味着什么都不做,当分区故障解决后,系统还是要保持保证CA。
2、BASE
BASE 是指基本可用(Basically Available)、软状态( Soft State)、最终一致性( Eventual Consistency),核心思想是即使无法做到强一致性(CAP 的一致性就是强一致性),但应用可以采用适合的方式达到最终一致性。
-
**BA - Basically Available 基本可用 ** 分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。
-
S - Soft State 软状态 允许系统存在中间状态,而该中间状态不会影响系统整体可用性。这里的中间状态就是 CAP 理论中的数据不一致。
-
E - Eventual Consistency 最终一致性 系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。
BASE 理论本质上是对 CAP 的延伸和补充,更具体地说,是对 CAP 中 AP 方案的一个补充:
-
CAP 理论是忽略延时的,而实际应用中延时是无法避免的。 这一点就意味着完美的 CP 场景是不存在的,即使是几毫秒的数据复制延迟,在这几毫秒时间间隔内,系统是不符合 CP 要求的。因此 CAP 中的 CP 方案,实际上也是实现了最终一致性,只是“一定时间”是指几毫秒而已。
-
AP 方案中牺牲一致性只是指发生分区故障期间,而不是永远放弃一致性。 这一点其实就是 BASE 理论延伸的地方,分区期间牺牲一致性,但分区故障恢复后,系统应该达到最终一致性。
总结:BASE解决了CAP中理论没有网络延迟,在BASE中用软状态和最终一致,保证了延迟后的一致性。BASE和 ACID 是相反的,它完全不同于ACID的强一致性模型,而是通过牺牲强一致性来获得可用性,并允许数据在一段时间内是不一致的,但最终达到一致状态。
3、数据一致性模型
分布式系统通过复制数据来提高系统的可靠性和容错性,并且将数据的不同的副本存放在不同的机器上,由于维护数据副本的一致性代价很高,因此许多系统采用弱一致性来提高性能,下面介绍常见的一致性模型:
-
强一致性 要求无论更新操作是在哪个数据副本上执行,之后所有的读操作都要能获得最新的数据。对于单副本数据来说,读写操作是在同一数据上执行的,容易保证强一致性。对多副本数据来说,则需要使用分布式事务协议。
-
弱一致性 在这种一致性下,用户读到某一操作对系统特定数据的更新需要一段时间,我们将这段时间称为"不一致性窗口"。
-
最终一致性 是弱一致性的一种特例,在这种一致性下系统保证用户最终能够读取到某操作对系统特定数据的更新(读取操作之前没有该数据的其他更新操作)。"不一致性窗口"的大小依赖于交互延迟、系统的负载,以及数据的副本数等。
系统选择哪种一致性模型取决于应用对一致性的需求,所选取的一致性模型还会影响到系统如何处理用户的请求以及对副本维护技术的选择等。后面将基于上面介绍的一致性模型分别介绍分布式事务的解决方案。
四、 常见分布式事务解决方案
1、 2PC(二阶段提交)方案 —— 强一致性
二阶段提交协议(Two-phase Commit,即2PC)是常用的分布式事务解决方案,即将事务的提交过程分为两个阶段来进行处理:准备阶段和提交阶段。
核心思想就是对每一个事务都采用先尝试后提交的处理方式,处理后所有的读操作都要能获得最新的数据,因此也可以将二阶段提交看作是一个强一致性算法。
说到2PC就不得不聊数据库分布式事务中的 XA Transactions。
在XA协议中分为两阶段:
-
第一阶段:事务管理器要求每个涉及到事务的数据库预提交(precommit)此操作,并反映是否可以提交.
-
第二阶段:事务协调器要求每个数据库提交数据,或者回滚数据。
阶段1:准备阶段
- 1、协调者向所有参与者发送事务内容,询问是否可以提交事务,并等待所有参与者答复。
- 2、各参与者执行事务操作,将undo和redo信息记入事务日志中(但不提交事务)。
- 3、如参与者执行成功,给协调者反馈yes,即可以提交;如执行失败,给协调者反馈no,即不可提交。
阶段2:提交阶段
如果协调者收到了参与者的失败消息或者超时,直接给每个参与者发送回滚(rollback)消息;否则,发送提交(commit)消息;参与者根据协调者的指令执行提交或者回滚操作,释放所有事务处理过程中使用的锁资源。(注意:必须在最后阶段释放锁资源)
缺点:
- 单点问题:事务管理器在整个流程中扮演的角色很关键,如果其宕机,比如在第一阶段已经完成,在第二阶段正准备提交的时候事务管理器宕机,资源管理器就会一直阻塞,导致数据库无法使用。
- 同步阻塞:在准备就绪之后,资源管理器中的资源一直处于阻塞,直到提交完成,释放资源。
- 数据不一致:两阶段提交协议虽然为分布式数据强一致性所设计,但仍然存在数据不一致性的可能,比如在第二阶段中,假设协调者发出了事务commit的通知,但是因为网络问题该通知仅被一部分参与者所收到并执行了commit操作,其余的参与者则因为没有收到通知一直处于阻塞状态,这时候就产生了数据的不一致性。
2、 3PC(三阶段提交)方案
三阶段提交协议,是二阶段提交协议的改进版本,与二阶段提交不同的是,引入超时机制。同时在协调者和参与者中都引入超时机制。
三阶段提交将二阶段的准备阶段拆分为2个阶段,插入了一个preCommit阶段,使得原先在二阶段提交中,参与者在准备之后,由于协调者发生崩溃或错误,而导致参与者处于无法知晓是否提交或者中止的“不确定状态”所产生的可能相当长的延时的问题得以解决。
阶段1:canCommit 协调者向参与者发送commit请求,参与者如果可以提交就返回yes响应(参与者不执行事务操作),否则返回no响应:
- 1、协调者向所有参与者发出包含事务内容的canCommit请求,询问是否可以提交事务,并等待所有参与者答复。
- 2、参与者收到canCommit请求后,如果认为可以执行事务操作,则反馈yes并进入预备状态,否则反馈no。
阶段2:preCommit 协调者根据阶段1 canCommit参与者的反应情况来决定是否可以基于事务的preCommit操作。根据响应情况,有以下两种可能。
情况1:阶段1所有参与者均反馈yes,参与者预执行事务:情况2:阶段1任何一个参与者反馈no,或者等待超时后协调者尚无法收到所有参与者的反馈,即中断事务:
阶段3:do Commit 该阶段进行真正的事务提交,也可以分为以下两种情况:
情况1:阶段2所有参与者均反馈ack响应,执行真正的事务提交:阶段2任何一个参与者反馈no,或者等待超时后协调者尚无法收到所有参与者的反馈,即中断事务:
注意:进入阶段3后,无论协调者出现问题,或者协调者与参与者网络出现问题,都会导致参与者无法接收到协调者发出的do Commit请求或abort请求。此时,参与者都会在等待超时之后,继续执行事务提交。
方案总结
-
优点 相比二阶段提交,三阶段贴近降低了阻塞范围,在等待超时后协调者或参与者会中断事务。避免了协调者单点问题,阶段3中协调者出现问题时,参与者会继续提交事务。
-
缺点 数据不一致问题依然存在,当在参与者收到preCommit请求后等待do commite指令时,此时如果协调者请求中断事务,而协调者无法与参与者正常通信,会导致参与者继续提交事务,造成数据不一致。
3、 TCC (Try-Confirm-Cancel)事务 —— 最终一致性
对于TCC的解释:
-
Try阶段:尝试执行,完成所有业务检查(一致性),预留必须业务资源(准隔离性)
-
Confirm阶段:确认执行真正执行业务,不作任何业务检查,只使用Try阶段预留的业务资源,Confirm操作满足幂等性。要求具备幂等设计,Confirm失败后需要进行重试。
-
Cancel阶段:取消执行,释放Try阶段预留的业务资源 Cancel操作满足幂等性Cancel阶段的异常和Confirm阶段异常处理方案基本上一致。
对于TCC来说适合一些:
- 强隔离性,严格一致性要求的活动业务。
- 执行时间较短的业务
示例:整个过程简单分为扣减库存,订单创建2个步骤,库存服务和订单服务分别在不同的服务器节点上。
4、 本地消息表 —— 最终一致性
方案的优点如下:
- 从应用设计开发的角度实现了消息数据的可靠性,消息数据的可靠性不依赖于消息中间件,弱化了对MQ中间件特性的依赖。
- 方案轻量,容易实现。
缺点如下:
- 与具体的业务场景绑定,耦合性强,不可公用。
- 消息数据与业务数据同库,占用业务系统资源。
- 业务系统在使用关系型数据库的情况下,消息服务性能会受到关系型数据库并发性能的局限。
5、 MQ事务 —— 最终一致性
RocketMQ的事务消息相对于普通MQ,相对于提供了2PC的提交接口,方案如下:
介绍完RocketMQ的事务消息方案后,由于前面已经介绍过本地消息表方案,这里就简单介绍RocketMQ分布式事务:
相比本地消息表方案,MQ事务方案优点是:
- 消息数据独立存储 ,降低业务系统与消息系统之间的耦合。
- 吞吐量由于使用本地消息表方案。
缺点是:
- 一次消息发送需要两次网络请求(half消息 + commit/rollback消息)
- 业务处理服务需要实现消息状态回查接口
6、 Saga事务 —— 最终一致性
值得补充的是,由于Saga模型中没有Prepare阶段,因此事务间不能保证隔离性,当多个Saga事务操作同一资源时,就会产生更新丢失、脏数据读取等问题,这时需要在业务层控制并发,例如:在应用层面加锁,或者应用层面预先冻结资源。
五、应用场景总结
-
2PC/3PC 依赖于数据库,能够很好的提供强一致性和强事务性,但相对来说延迟比较高,比较适合传统的单体应用,在同一个方法中存在跨库操作的情况,不适合高并发和高性能要求的场景。
-
TCC 适用于执行时间确定且较短,实时性要求高,对数据一致性要求高,比如互联网金融企业最核心的三个服务:交易、支付、账务。
-
本地消息表/MQ事务 都适用于事务中参与方支持操作幂等,对一致性要求不高,业务上能容忍数据不一致到一个人工检查周期,事务涉及的参与方、参与环节较少,业务上有对账/校验系统兜底。
-
Saga事务 由于Saga事务不能保证隔离性,需要在业务层控制并发,适合于业务场景事务并发操作同一资源较少的情况。 Saga相比缺少预提交动作,导致补偿动作的实现比较麻烦,例如业务是发送短信,补偿动作则得再发送一次短信说明撤销,用户体验比较差。Saga事务较适用于补偿动作容易处理的场景。
-------------------------------------------------------------------------------------------