设为首页 加入收藏

TOP

分布式系统一致性保障方案总结(一)
2019-09-17 18:07:35 】 浏览:57
Tags:分布式 系统 一致性 保障 方案 总结

群里经常卧虎藏龙,转载一篇百度大牛,投稿原创文章,大家交流学习 ,欢迎更多朋友投稿,发布原创文章和干货和大家分享交流。

 

引言

       在互联网系统中,理想的情况下,肯定是希望系统能够同时满足“一致性”、“可用性”和“分区容忍性”。 但是基于熟悉的CAP定律也好,还是BASE理论, 我们知道,在实际情况中是不可能实现的。而在金融领域,一致性是最为关注的特性,任何情况下都必须满足一致性。关于CAP定律和BASE理论,本文不再介绍,有兴趣的同学可以自行百度一下。本文重点来阐述下关于一致性的方案,包括强一致性和最终一致性。 而在互联网领域, 很多情况下都是牺牲强一致性,来达到高可用性,统往往只需要保证“最终一致性”,只要这个最终时间是在用户可以接受的范围内即可。

 

数据库本地事务

       数据库事务肯定是强一致性的方案,而且是一致性最简单的方案,因为一致性是数据库的事务来保证的,业务层不需要关心细节。比较典型的应用是在返现场景下,针对带有返现的交易的退款,需要一次性退两笔交易单,采用的就是通过数据库本地事务来完成的。具体如下:

 

用户A花了100元购买商户B的商品,购买结束后返现给用户A 2元。 这是两笔交易,原始交易是100元,返现交易是2元。 那么发生退款时,需要保证两笔交易同时都退款。这个就是直接采用数据库本地事务实现的,即一次退款请求,两笔交易同时退款。

 

总结: 数据库事务的优点是简单,业务层关心的很少。但是对于一个可用性很高的系统来说,所有的业务都揉在数据库事务执行,会让事务非常的复杂,不利于系统的扩展和维护。

 

两阶段提交

        除了数据库能够保证本地的一致性,对于互联网系统来说,更多是分布式系统。提到分布式系统,必然提到分布式事务。而分布式事务中,就不得不介绍两阶段提交协议(2pc)。 而在核心系统,两阶段提交的方案主要应用在分布式数据库NesioDB和交易账务分离的柔性事务中

       分布式数据库NesioDB是由百度DBA和百度钱包联合开发的,支持分布式事务的数据库,目前已经应用在百度钱包的核心交易业务上,并稳定运行两年。该数据库的设计要求是让使用者能够像使用单机数据库一样的使用分布式数据库,因此实现的分布式事务,满足单机事务的ACID原则。关于分布式事务的一致性,采用的就是两阶段提交的方式来实现的,并且满足分布式事务模型。如下图所示。

第一阶段是准备阶段。

 

 

 

 

DTM 通知所有参与事务的各个 RM,给每个 RM 发送 prepare 消息。RM 接收到消息后进入准备阶段后,要么直接返回失败,要么创建并执行本地事务,写本地事务日志(redo 和 undo 日志),但是 不提交(此处只保留最后一步耗时最少的提交操作给第二阶段执行)。

第二阶段是提交/回滚阶段。

 

 

 

DTM 收到 RM 准备阶段的失败消息或者获取 RM 返回消息超时,则直接给 RM 发送回滚(rollback)消息,否则发送提交(commit)消息。RM 根据 TM 的指令执行提交或者回滚,执行完成后释放所有事务处理过程中使用的锁(最后阶段释放锁)。

数据库层面的两阶段提交,可以用来保证分布式事务的一致性,使得使用者使用分布式事务和单机事务一样方便。而两阶段提交的另外一种实现,即TCC(Try-Confirm-Cancel), 也就是业务层面的柔性事务。 交易和账务分离的一致性实现,就是采用这种柔性事务来完成的。首先来说说柔性事务,它涉及 3 个模块,主业务、从业务 和 活动管理器(协作者)。

下面这张图是有关柔性事务一张经典的图。

 

第一阶段:主业务服务分别调用所有从业务服务的 try 操作,并在活动管理器中记录所有从业务服务。当所有从业务服务 try 成功或者某个从业务服务 try 失败时,进入第二阶段。

第二阶段:活动管理器根据第一阶段从业务服务的 try 结果来执行 confirm 或 cancel 操作。如果第一阶段所有从业务服务都 try 成功,则协作者调用所有从业务服务的 confirm 操作,否则,调用所有从业务服务的 cancel 操作。

在第二阶段中,confirm 和 cancel 同样存在失败情况,所以需要对这两种情况做 异常处理以保证数据一致性。

1.  Confirm 失败:则回滚所有 confirm 操作并执行 cancel 操作。

2.  Cancel 失败:从业务服务需要提供自动 cancel 机制,以保证 cancel 成功。

如果对应到交易和账务分离的项目中,流程如下:

第一阶段:  主业务服务调用交易和账务执行try的操作,交易开启事务,做业务上的判断和写入,但是不提交事务。账务层面做资源的锁定。

第二阶段:  账务资源锁定成功,交易提交事务成功,然后发送confirm 给账务。  如果交易提交失败,则发送cancel对资源进行释放。如果在confirm或者cancel出现异常情况下,同样需要对异常进行处理来保证数据一致性。

 

总结: 这种方式实现难度不算太高,比较适合传统的单体应用,在同一个方法中存在跨库操作的情况。

 

回滚机制

     在分布式架构中,功能 X,需要去协调后端的 A、B 甚至更多的原子服务。那么问题来了,假如 A 和 B 其中一个调用失败了,那可怎么办呢? 这个时候,可以用回滚机制来保证一致性。 该机制应用在钱包配合信贷做的联合放贷项目中。 该项目中总共有两个原子操作,如下图所示。

 

 

两个原子操作,分别是资金归集和资金到卡。所谓资金归集,是将商户A的钱和商户B的钱归集到中间商户C。而资金到卡,是将中间商户C的钱,通过银行系统打入到D用户的银行卡。这两个操作要满足一致性,即资金归集成功,然后打款到用户的卡成功。或者是商户A和B的钱没变化,资金到卡失败。 总而言之,是不允许资金停留在中间商户C的。

       针对这种情况,通过回滚机制,提供一个强大的回滚操作来实现上述的一致性。比如资金归集成功,而资金到卡失败,那么对归集的资金操作做回滚处理,也就是资金从中间商户C分别回到商户A和B中。

 

总结: 这种方式缺点比较多,通常在复杂场景下是不推荐使用的,除非是非常简单的场景,非常容易提供回滚,而且依赖的服务也非常少的情况。这种实现方式会造成代码量庞大,耦合性高。而且非常有局限性,因为有很多的业务是无法很简单的实现回滚的,如果串行的服务很多,回滚的成本实在太高。

 

本地消息表

       这种实现方式的思路,其实是源于 ebay,后来通过支付宝等公司的布道,在业内广泛使用。其基本的设计思想是将远程分布式事务拆分成一系列的本地事务。如果不考虑性能及设计优雅,借助关系型数据库中的表即可实现。本地消息的方式,在应用在钱包非核心业务异步化改造项目中。该项目当时改造的方案如下:

1. &nb

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇大型网站系统与java中间件实践-阅.. 下一篇[Medium翻译]RESTful API权威设计..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目