分布式事务解决方案
- 分布式事务是指发生在多个数据节点之间的事务,分布式事务比单机事务要复杂得多。在分布式系统中,各个节点之间是相互独立的,需要通过网络进行沟通和协调。由于存在事务机制,可以保证每个独立节点上的数据操作可以满足ACID
- 如果想让分布式部署的多个节点中的数据保持一致性,那么就要保证在所有节点上数据的写操作,要么全部都执行,要么全部都不执行。一台机器在执行本地事务的时候无法知道其他机器中的本地事务的执行结果,所以它也就不知道本次事务到底应该commit还是rollback。所以,常规的解决办法就是引入一个协调者的组件来统一调度所有分布式节点的执行
强一致事务解决方案
- 强一致事务解决方案:在任何时间,读取任意节点上的数据,都是最新写入的数据
- DTP模型(全局事务模型)
- 2PC模型(二阶段提交,Two-phase Commit Protocol):两阶段提交分为两个阶段:准备阶段(Prepare Phase)和提交阶段(Commit Phase)。
- 3PC模型(三阶段提交协议,Three-phase_commit_protocol):是在 2PC 之上扩展的提交协议,主要是为了解决两阶段提交协议的阻塞问题,从原来的两个阶段扩展为三个阶段,增加了超时机制
- 强一致事务解决方案优点
- 数据一致性比较高
- 在任意时刻都能够查询到最新写入的数据
- 强一致事务解决方案缺点
- 存在性能问题,在分布式事务未完全提交和回滚之前,应用程序不会查询到最新的数据
- 实现复杂
- 牺牲可用性
- 不适合高并发场景
2PC模型
- 2PC两阶段执行流程分为准备阶段和提交阶段
-
准备阶段:在准备阶段,协调者将通知事务参与者准备提交或取消事务,写本地的redo和undo日志,但不提交,然后进入表决过程。在表决过程中,参与者将告知协调者自己的决策:同意(事务参与者本地作业执行成功)或取消(本地作业执行故障)
- 协调者协议流程
- 写本地日志BEGIN_COMMIT,并进入WAIT状态。
- 向所有参与者发送VOTE_REQUEST消息。
- 等待并接收参与者发送的对VOTE_REQUEST的响应。参与者响应VOTE_ABORT或VOTE_COMMIT消息给协调者。
- 协调者协议流程
-
提交阶段:在该阶段,协调者将基于第一个阶段的投票结果进行决策:提交或取消。当且仅当所有的参与者同意提交事务,协调者才通知所有的参与者提交事务,否则协调者将通知所有的参与者取消事务。参与者在接收到协调者发来的消息后将执行相应的操作。
- 协调者协议流程如下
- 若收到任何一个参与者发送的VOTE_ABORT消息。
- 写本地GLOBAL_ABORT日志,进入ABORT状态。
- 向所有的参与者发送GLOBAL_ABORT消息。
- 若收到所有参与者发送的VOTE_COMMIT消息。
- 写本地GLOBAL_COMMIT日志,进入COMMIT状态。
- 向所有的参与者发送GLOBAL_COMMIT消息。
- 等待并接收参与者发送的对GLOBAL_ABORT消息或GLOBAL_COMMIT消息的确认响应消息,一旦收到所有参与者的确认消息,写本地END_TRANSACTION日志,流程结束。
- 协调者协议流程如下
-
2PC存在的问题
- 同步阻塞。执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。在提交请求阶段,需要预留资源,在资源预留期间,其他人不能操作
- 单点故障。由于协调者的重要性,一旦协调者发生故障,参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题。
- 数据不一致。在两阶段提交的阶段二中,当协调者向参与者发送COMMIT请求之后,发生了局部网络异常或者在发送COMMIT请求过程中协调者发生了故障,这会导致只有一部分参与者接收到了COMMIT请求。而在这部分参与者接收到COMMIT请求之后就会执行COMMIT操作。但是其他部分未接收到COMMIT请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据不一致性的现象(脑裂)
3PC模型
-
三阶段提交协议是两阶段提交协议的改良版。是在协调者和参与者中都引入超时机制,并且把两阶段提交协议的第一个阶段分成了两步。因此,三阶段提交协议包含询问、锁资源、提交三个步骤。
-
两阶段提交协议存在的问题是,协调者在某些时刻如果失败了,整个事务就会阻塞。
-
三阶段提交分为三个阶段
- CanCommit阶段
- PreCommit阶段
- DoCommit阶段
-
CanCommit,协调者的流程(该流程与两阶段提交协议类似)
- 写本地日志BEGIN_COMMIT,并进入WAIT状态。
- 向所有参与者发送VOTE_REQUEST消息。
- 等待并接收参与者发送的对VOTE_REQUEST的响应。参与者响应VOTE_ABORT或VOTE_COMMIT消息给协调者
-
PreCommit:协调者将通知事务参与者准备提交或取消事务,写本地的redo和undo日志,但不提交。协调者的流程(与两阶段提交协议相比,多了一个PRECOMMIT状态)
- 若收到任何一个参与者发送的VOTE_ABORT消息。
- 写本地GLOBAL_ABORT日志,进入ABORT状态。
- 向所有的参与者发送GLOBAL_ABORT消息
- 若收到所有参与者发送的VOTE_COMMIT消息。
- 写本地PREPARE_COMMIT日志,进入PRECOMMIT状态。
- 向所有的参与者发送PREPARE_COMMIT消息。
- 等待并接收参与者发送的对GLOBAL_ABORT消息或PREPARE_COMMIT消息的确认响应消息。一旦收到所有参与者的GLOBAL_ABORT确认消息或者超时没有收到,写本地END_TRANSACTION日志,流程结束,则不再进入DoCommit阶段。如果收到所有参与者的PREPARE_COMMIT确认消息,则进入DoCommit阶段。
-
DoCommit:在该阶段,协调者协议流程如下
- 向所有参与者发送GLOBAL_COMMIT消息。
- 等待并接收参与者发送的对GLOBAL_COMMIT消息的确认响应消息,一旦收到所有参与者的确认消息,写本地END_TRANSACTION日志,流程结束
- 在DoCommit阶段,如果参与者无法及时接收到来自协调者的GLOBAL_COMMIT请求,会在等待超时之后继续进行事务的提交
-
三阶段提交的缺陷:相对于2PC,3PC主要解决单点故障问题,并减少阻塞,因为一旦参与者无法及时收到来自协调者的信息,他会默认执行COMMIT。而不会一直持有事务资源并处于阻塞状态。但是这种机制也会导致数据一致性问题,因为,由于网络原因,协调者发送的ABORT响应没有及时被参与者接收,那么参与者在等待超时之后执行了COMMIT操作。这样就和其他接到ABORT命令并执行回滚的参与者之间存在数据不一致的情况
-
无论是二阶段提交还是三阶段提交都无法彻底解决分布式的一致性问题。
最大努力通知
-
与补偿模式不同,最大努力通知模式本质上是一种通知类的实现方案。
-
最大努力通知模式的基本思路是通知发送方在完成业务处理后向通知接收方发送通知消息。通知消息允许丢失,所以当消息的接收方没有成功消费消息时,消息的发送方还需要进行重复发送直到消费者消费成功或达到某种发送终止条件。一般,消息发送方可以设置复杂的通知规则,如采用15s、3m、10m、30m、1h、2h、6h、15h等阶梯式时间的通知方式。
-
在最大努力通知模式中,通知接收方也可以使用主动方所提供的查询和对账接口获取数据,用于恢复通知失败所导致的业务数据。
-
最大努力通知模式在通知类场景应用广泛,以支付宝为例,通过回调商户提供的回调接口,通过多次通知、查询对账等手段完成交易业务平台间的商户通知。最大努力通知模式适合于对业务最终一致性的时间敏感度比较低的场景,一般用于类似支付宝与商户集成类跨企业的业务活动。
-
最大努力通知包含如下组件
- 查询组件:通知发送方处理业务并把业务记录保存起来,查询组件提供查询入口供通知接收方主动查询业务数据,避免数据丢失。
- 确认组件:当通知接收方成功接收到通知时,需要与通知发送方确认通知已被正常接收。确认组件接收到确认消息之后就会更新业务记录中的状态,通知组件根据状态就不需要再发送通知。
- 通知组件:通知组件根据业务记录向通知接收方发送通知,在发送通知的过程中需要保存通知记录,并根据业务记录的状态以及现有的通知记录确定下一次发送通知的策略,例如通过通知记录可判断是否已经到达通知上限或决定下一次发送通知的时间是在5分钟之后还是1小时之后。