查看原文
其他

未来架构| 云原生时代的分布式事务

张亮 高可用架构 2019-11-28

导读:微服务拆分之后,在很多场景,分布式事务无法避免。而分布式事务是行业内的一个技术难点,没有一种完美的方案。分布式事务有哪些解决方案?又应该如何落地?听京东数科数据研发负责人张亮讲述分布式事务的点点滴滴。本文适合研发工程师,技术经理,架构师反复阅读。
前文提到过,数据库事务是需要满足ACID(原子性、一致性、隔离性、持久性)四个特性的。

在单一数据节点中,事务仅限于对单一数据库资源进行访问控制,这种事务称为本地事务。几乎所有成熟的关系型数据库都提供了对本地事务的原生支持。但是在基于微服务的分布式应用环境下,越来越多的应用场景要求将多个服务的访问以及相对应的数据库资源纳入同一个事务,因此,分布式事务应运而生。

关系型数据库虽然对本地事务提供了完美的ACID原生支持。但在分布式的场景下,它却成为了系统性能的瓶颈。如何让数据库在分布式场景下满足ACID特性,或找寻相应的替代方案,是分布式事务的重点工作内容。


  • XA协议

最早的分布式事务模型是由 X/Open 国际联盟提出的 X/Open Distributed Transaction Processing(DTP)模型,也称为XA协议。

XA协议通过一个全局事务管理器与多个资源管理器进行交互。全局事务管理器负责管理全局事务状态和参与事务的资源,资源管理器则负责具体的资源操作,XA协议与应用程序的关系如图9-9所示。

XA协议使用两阶段提交来保证分布式事务的原子性以,它将提交过程分为准备阶段和提交/回滚阶段。两阶段提交也是XA协议的标准实现。

在准备阶段,全局事务管理器向每个资源管理器发送准备消息,用于确认本地事务操作成功与否。在提交阶段,若全局事务管理器收到了所有资源管理器回复的成功消息,则向每个资源管理器发送提交消息,否则发送回滚消息。资源管理器根据接收到的消息对本地事务进行提交或回滚。图9-10展示了XA协议的事务流程。

开启XA全局事务后,所有子事务会按照本地默认的隔离级别锁定资源,并记录undo和redo日志,然后由TM发起prepare投票,询问所有子事务是否可以进行提交。当所有子事务反馈的结果为“yes”时,TM再发起commit;若其中任何一个子事务反馈的结果为“no”,TM则发起rollback;如果在prepare阶段的反馈结果为yes,而commit的过程中出现宕机等异常,则在节点服务重启后,可根据XA recover再次进行commit补偿,以保证数据的一致性。

基于XA协议实现的分布式事务对业务的侵入性很弱。它最大的优势就是对使用方透明,用户可以像使用本地事务一样使用基于XA协议的分布式事务。XA协议能够严格保障事务的ACID特性。

严格保障事务的ACID特性是一把双刃剑。事务执行过程中需要将所需资源全部锁定,更加适用于执行时间确定的短事务。对于长事务来说,在整个事务进行期间独占数据将导致依赖热点数据的业务系统的并发性能明显衰退。因此,在高并发性能至上的场景中,基于XA协议的分布式事务并不是最佳选择。

  • 柔性事务

如果将实现了ACID特性的事务称为刚性事务的话,那么基于BASE事务要素的事务则称为柔性事务。BASE是基本可用(Basically Available)、柔性状态(Soft state)和最终一致性(Eventually consistent)的缩写。

* 基本可用保证分布式事务参与方不一定同时在线。

* 柔性状态允许系统状态更新有一定的延时,客户不一定能够察觉。

* 最终一致性通常通过消息可达的方式来保证。

在ACID事务中,对隔离性的要求很高,在事务执行的过程中必须将所有的资源锁定。柔性事务的理念则是通过业务逻辑将互斥锁操作从资源层面移至业务层面,通过放宽对强一致性的要求来换取系统吞吐量的提升。

由于在分布式系统中可能会出现超时重试的情况,因此柔性事务中的操作必须是幂等的,需要通过幂等来避免多次请求所带来的问题。实现柔性事务的方案主要有最大努力送达、Saga 、TCC和消息驱动,下面我们具体来看。

1.最大努力送达

最大努力送达是最简单的柔性事务方案,它适合用于“对数据库的操作最终一定能够成功”的场景,由NewSQL自动记录执行失败的SQL,并反复尝试,直至执行成功。

使用最大努力送达方案的柔性事务是没有回滚功能的。这种类型的柔性事务实现起来最为简单,但是对场景的要求十分苛刻。这种策略的优点是无锁定资源时间,性能损耗小。缺点是尝试多次提交失败后无法回滚,它仅适用于事务最终一定能够成功的业务场景。因此它是通过对事务回滚功能的妥协来换取性能提升的。

2.Saga

Saga源于Hector Garcaa-Molrna和Kenneth Salem发表的论文Sagas。Saga方案更适合用于长事务场景。Saga模型将一个分布式事务拆分为多个本地事务,每个本地事务都有相应的执行模块(Transaction)和补偿模块(Compensation),任和一个本地事务出错时,都可以通过调用相关的补充方法实现事务的最终一致性。

当每个Saga子事务序列 T1,T2,…,Tn都有对应的补偿定义C1,C2,…,Cn-1时,Saga系统可以保证如下状态。

  • 子事务序列可以完成。这是事务的最佳情况,即无须回滚。

  • 或者序列 T1, T2, …, Tx,和Cx, …, C2, C1(其中x小于n)可以完成。这种状态能够保证,当回滚发生时补偿操作按照正向操作的相反顺序依次执行。

Saga模型同时支持正向恢复和逆向恢复。正向恢复是指重试当前失败的事务,它的实现前提是每个子事务最终都能够执行成功;逆向恢复则是指在任意一个子事务失败时补偿所有已完成的事务。

显然,正向恢复没有必要提供补偿事务,如果在业务中的子事务最终总会成功,那么正向恢复能够降低Saga模型的使用复杂度。另外,即使补偿事务难以实现,正向恢复也是不错的选择。

虽然在理论上来讲,补偿事务永不失败。但是在分布式的世界中,服务器可能会宕机,网络可能会失败,数据中心也可能会停电。因此,需要提供故障恢复后的回退机制,比如人工干预机制等。

Saga模型没有XA协议中的准备阶段,因此事务没有实现隔离性。如果两个Saga事务同时操作同一资源则会产生更新丢失、脏数据读取等问题,这时就需要使用Saga作为事务管理机制的应用程序,在应用层面加入资源锁定的逻辑了。

3.TCC

TCC(Try-Confirm-Cancel)分布式事务模型通过对业务逻辑进行分解来实现分布式事务。顾名思义,TCC事务模型需要业务系统提供以下三种业务逻辑。

  • Try:完成业务检查,预留业务所需的资源。Try操作是整个TCC的精髓,可以灵活选择业务资源锁的粒度。

  • Confirm:执行业务逻辑,直接使用Try阶段预留的业务资源,无须再次进行业务检查。

  • Cancel:释放Try阶段预留的业务资源。

TCC模型仅提供两阶段原子提交协议,保证分布式事务的原子性。事务的隔离交给业务逻辑来实现。TCC 模型的隔离性思想是,通过对业务的改造将对数据库资源层面加锁上移至对业务层面加锁,从而释放底层数据库锁资源,拓宽分布式事务锁协议,提高系统的并发性。

虽然在柔性事务中,TCC事务模型的功能最强,但需要应用方负责提供实现Try、Confirm和Cancel操作的三个接口,供事务管理器调用,因此业务方改造的成本较高。

以A账户向B账户汇款100元为例,图9-11展示了TCC的流程。汇款服务和收款服务需要分别实现Try、Confirm、Cancel这三个接口,并在业务初始化阶段将这三个接口的实现注入TCC事务管理器。

  • 汇款服务

— Try:检查A账户的有效性;检查A账户的余额是否充足;从A账户中扣减100元,并将状态置为“转账中”;预留扣减资源,将“从A账户向B账户转账100元”这个事件存入消息或日志。

— Confirm:不做任何操作。

— Cancel:A账户增加100元;从日志或消息中释放扣减资源。

  • 收款服务

— Try:检查B账户的有效性。

— Confirm:读取日志或者消息,B账户增加100元;从日志或消息中释放扣减资源。

— Cancel:不做任何操作。

由此可以看出,TCC模型对业务的侵入性较强,改造的难度较大。

4.消息驱动

消息一致性方案是通过消息中间件保证上下游应用数据操作一致性的。基本思路是,将本地操作和发送消息放在同一个本地事务中,下游应用从消息系统订阅该消息,收到消息后执行相应的操作,本质上是依靠消息的重试机制达到最终一致性的。图9-12展示了消息驱动的事务模型。

消息驱动的缺点是,耦合度高,需要在业务系统中引入消息中间件,将导致系统复杂度增加。

基于ACID的强一致性事务和基于BASE的最终一致性事务都不是“银弹”,只有在最适合的场景中才能发挥它们的最大长处。

表9-4详细对比了分布式事务之间的区别,可以帮助开发者进行技术选型。由于消息驱动与业务系统的耦合度较高,因此不列入对比表格。

表9-4 分布式事务对比

由于应用场景不同,因此需要开发者合理地在性能与功能之间权衡各种分布式事务。

强一致性的事务与柔性事务的API和功能并不完全相同,因此不能在它们之间自由地透明切换。在开发决策阶段,必须要在强一致的事务和柔性事务之间抉择,因此设计和开发成本大幅增加。

基于XA协议的强一致事务使用起来相对简单,但是无法很好地应对互联网的短事务和高并发场景;柔性事务则需要开发者对应用进行改造,接入成本非常高,并且需要开发者自行实现资源锁定和反向补偿。

一味地追求强一致性未必是最合理的解决方案。对于分布式系统来说,建议使用“外柔内刚”的设计方案。外柔指的是在跨数据分片的情况下使用柔性事务,保证数据最终一致,并且换取最佳性能;内刚则是指在同一数据分片内使用本地事务,以满足ACID特性。

本文节选自电子工业出版社《未来架构:从服务化到云原生》第9章,由电子工业出版社博文视点授权。本书对快速演进中的云原生数据架构、典型分布式数据库中间件进行了剖析,重点介绍Service Mesh等新兴概念,创新性地提出了Database Mesh的理念,深度揭秘Apache项目——ShardingSphere,精彩内容层出不穷,知识概念全然领先一代。


基于篇幅的考虑,部分内容进行了简化,想了解本书全部详细内容,可以扫二维码直接购买。


本次活动我们采取文章留言送书的活动。在本周末前,留言点赞数最高的前 3 名我们将免费赠送本书!图书由电子工业出版社博文视点提供。


参考阅读:



张亮将作为中间件专场的出品人出席2019年GIAC深圳站,并做关于数据中间件相关演讲。参加2019年GIAC深圳站,可以了解业界动态,和业界专家近距离接触。

参加 GIAC,盘点2019年最新技术,目前购买7.5折优惠 ,多人购买有更多优惠。点击“阅读原文”了解大会更多详情。

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存