分布式系统一致性保障方案总结

标签: 分布 系统 一致性 | 发表时间:2017-09-21 11:43 | 作者:
出处:https://mp.weixin.qq.com

猫友会群里经常卧虎藏龙,转载一篇百度大牛,投稿原创文章,大家交流学习 ,文末有作者个人公众号。欢迎更多猫友投稿,发布原创文章和干货和大家分享交流。





引言

       在互联网系统中,理想的情况下,肯定是希望系统能够同时满足“一致性”、“可用性”和“分区容忍性”。 但是基于熟悉的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.   核心业务实时写入交易表

2.   非核心业务非实时异步写入交易表按照用户维度的交易查询表。

交易表是交易维度的,而为了满足用户的查询性能,需要备份复制相同的按照用户维度的交易查询表。 从业务属性上来看,交易表是核心业务,交易查询表是非核心业务(查询使用)。而实现上,交易表是核心数据库,而查询表则属于非核心数据库。 但是, 这两者需要满足一致性。 关于这类一致性保障,如果有不丢消息的消息队列,则很容易解决。万一没有这类消息队列呢? 其实,使用本地消息表,也一样可以解决。

如图所示,是利用本地消息表保持最终一致性的应用。 具体如下:

1.   业务A将本地消息和A业务数据以本地事务的方式写入DB1;

2.   业务A写完本地事务后,发送消息给MQ。

3.   MQ推送消息给业务B,业务B执行消息,写入DB2.

4.   由于MQ不能保证消息不丢,如果消息丢失了,则需要通过业务C,读取DB1的消息,然后rpc发送给业务B重新执行。

当然,如何判断DB1的消息已经消费,这个可以通过DB2的事务执行结果来判断。



总结: 上诉的方式是一种非常经典的实现,基本避免了分布式事务,实现了“最终一致性”。但是,关系型数据库的吞吐量和性能方面存在瓶颈,频繁的读写消息会给数据库造成压力。所以,在真正的高并发场景下,该方案也会有瓶颈和限制的。



补偿机制

       补偿机制在分布式系统中,应用最为广泛。在钱包应用的场景比较多,比如核心收银台和付款到卡。 核心收银台中,当请求银行扣款,扣款成功后,自身系统挂掉了。这个时候就会有一个后台程序,我们也称作补单程序来开始处理这类流程,让原来中间断掉的流程继续走下去。

      一般成熟的系统中,对于级别较高的服务和接口,整体的可用性通常都会很高。如果有些业务由于瞬时的网络故障或调用超时等问题,那么这种补偿机制其实是非常有效的。



总结

       本文通过核心系统的几个具体实际项目,阐述了如何保证分布式系统的一致性。每一种方案都有一定的特征和应用场景。 其实分布式系统的事务一致性本身是一个技术难题,目前没有一种很简单很完美的方案能够应对所有场景。具体还是要使用者根据不同的业务场景去抉择。




相关 [分布 系统 一致性] 推荐:

分布式系统的事务及一致性模型

- Roger - NoSQLFan
下面PPT出自10gen的产品和工程高级副总裁 Roger Bodamer ,参加过Mongo Beijing的人应该记得会上的大个子. 下面PPT 主要就分布式系统的事务及一致性模型进行了分析和讨论. 对分布式存储在CAP原理下的选择和实现进行了描述. Google Megastore系统事务机制.

分布式系统中的事务一致性问题

- - CSDN博客架构设计推荐文章
在分布式系统中,我们经常遇到多数据副本保持一致的问题,在我们所能找到的资料中该问题讲的很笼统,模模糊糊的,把多个问题或分类糅合在一起,难以理解. 在思考和翻阅资料后,通俗地把一致性的问题可分解为2个问题:. 1、任何一次修改保证数据一致性. 在弱一致性的算法,不要求每次修改的内容在修改后多副本的内容是一致的,对问题1的解决比较宽松,更多解决问题2,该类算法追求每次修改的高度并发性,减少多副本之间修改的关联性,以获得更好的并发性能.

关于分布式系统的数据一致性问题

- - 互联网 - ITeye博客
现在先抛出问题,假设有一个主数据中心在北京M,然后有成都A,上海B两个地方数据中心,现在的问题是,假设成都上海各自的数据中心有记录变更,需要先同步到主数据中心,主数据中心更新完成之后,在把最新的数据分发到上海,成都的地方数据中心A,地方数据中心更新数据,保持和主数据中心一致性(数据库结构完全一致).

分布式系统数据一致性的6种方案(转)

- - 企业架构 - ITeye博客
编者按:本文由「高可用架构后花园」群讨论整理而成,后花园是一个面向架构师的增值服务,如需了解,请关注「高可用架构」后回复 VIP.                                                                                 问题的起源.

分布式系统的一致性算法简介

- - 互联网 - ITeye博客
在分布式系统中,我们经常遇到多数据副本保持一致的问题,在我们所能找到的资料中该问题讲的很笼统,模模糊糊的,把多个问题或分类糅合在一起,难以理解. 在思考和翻阅资料后,通俗地把一致性的问题可分解为2个问题:. 1、任何一次修改保证数据一致性. 2、多次数据修改的一致性. 在弱一致性的算法,不要求每次修改的内容在修改后多副本的内容是一致的,对问题1的解决比较宽松,更多解决问题2,该类算法追求每次修改的高度并发性,减少多副本之间修改的关联性,以获得更好的并发性能.

分布式系统一致性保障方案总结

- -
猫友会群里经常卧虎藏龙,转载一篇百度大牛,投稿原创文章,大家交流学习 ,文末有作者个人公众号. 欢迎更多猫友投稿,发布原创文章和干货和大家分享交流.        在互联网系统中,理想的情况下,肯定是希望系统能够同时满足“一致性”、“可用性”和“分区容忍性”. 但是基于熟悉的CAP定律也好,还是BASE理论, 我们知道,在实际情况中是不可能实现的.

【分布式系统工程实现】CAP理论及系统一致性

- hikerlive - 淘宝核心系统团队博客
印象中CAP理论开始流行是从Amazon Dynamo的论文开始的,Amazon的CTO还在他的博客中介绍了最终一致性的概念,从此以后,各种会议和交流中都少不了CAP的影子. 然而,对于分布式系统工程设计和开发来说,CAP意味着什么呢. CAP 理论由 Berkerly 的 Brewer 教授提出,三者的含义如下:.

一致性哈希算法及其在分布式系统中的应用

- BeerBubble - 博客园-EricZhang's Technology Blog
本文将会从实际应用场景出发,介绍一致性哈希算法(Consistent Hashing)及其在分布式系统中的应用. 首先本文会描述一个在日常开发中经常会遇到的问题场景,借此介绍一致性哈希算法以及这个算法如何解决此问题;接下来会对这个算法进行相对详细的描述,并讨论一些如虚拟节点等与此算法应用相关的话题.

保证分布式系统数据一致性的6种方案

- - 互联网 - ITeye博客
摘要: 在电商等业务中,系统一般由多个独立的服务组成,如何解决分布式调用时候数据的一致性. 具体业务场景如下,比如一个业务操作,如果同时调用服务 A、B、C,需要满足要么同时成功;要么同时失败. 在电商等业务中,系统一般由多个独立的服务组成,如何解决分布式调用时候数据的一致性. 具体业务场景如下,比如一个业务操作,如果同时调用服务 A、B、C,需要满足要么同时成功;要么同时失败.

分布式系统中的一致性协议之两阶段提交协议(2PC)

- - 断尘居
 两阶段提交协议是很常见的解决分布式事务的方式,他可以保证分布式事务中,要么所有参与的进程都提交事务成功,要么都取消事务,这样做可以在分布式环境中保持ACID中A(原子性).      在两阶段提交协议中,包含了两种角色:协调者与参与者. 参与者就是实际处理事务的机器,而协调者就是其中一台单独的处理分布式事务的机器.