您当前的位置: 首页 > 

蔚1

暂无认证

  • 0浏览

    0关注

    4753博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

最终一致性:BASE论文笔记

蔚1 发布时间:2019-12-24 23:30:35 ,浏览量:0

现在的系统很多时候都采用了分布式的架构。这种架构之下,各个服务之间管理着自身独立的数据。一个完整的业务往往跨越多个服务。在传统单机应用时代,数据的一致性可以依靠数据库事务保证。但是在分布式下,数据的一致性问题却变得麻烦起来。本文通过对 BASE 论文的解读,来阐述其提到的通过最终一致性的方案思路来处理分布式下数据一致性问题。

简述

Base 论文是 ebay 的架构师于 2008 年提交的一篇论文。主要用来阐述在分布式架构设计下,基于 BASE 的设计思想和方案。所谓 BASE 就是 basically available(基本的可用性),soft state(软状态,所谓的软状态,指的是暂时的不一致,后文会详细展开),eventually consistent(最终一致性)。在分布式领域,有著名的 CAP 理论,也就是一致性,可用性,分区容错性即可靠性,这三者无法同时获得。而 base 理论就是在牺牲部分一致性的基础上,来达到可用性的大幅提升的一种方案理念。

分区容错性

这是 BASE 里相对简单好操作的一个地方。比如我们需要存储用户数据,通过部署多个物理实例,将用户数据均匀的分散在其上。即使其中的一个服务器发生宕机,也不会影响到其他的数据。容忍了局部失败而提供了整体上一定的容错性。容错的问题通过将数据部署多个节点来保证。那就带来下面的问题,数据的一致性如何保证。传统的业务解决方式就是 2PC。依赖于数据库提供的 XA 事务来实现分布式下数据一致性。

传统的数据库事务方式在分布式领域的问题

考虑一个这样的场景,A 给 B 进行转账。2 个用户在不同的银行,他们的数据库部署在不同的物理节点。而转账是一个事务操作。传统意义上有所谓的分布式事务,也就是 2PC 这种协议来保证分布式情况下的事务。但是 2PC 协议的开销很大,不利于在大规模的情况下的性能表现。并且 XA 只是数据库层面的协议,如果应用本身是分布式的,还需要额外的落地支持。在实现上也不简单。

BASE 方式来解决

这个转账例子有两个数据库,如果 A 成功了,B 失败了,此时就需要回滚 A。如果我们不回滚,而是重试直到 B 成功也是一种可行的方案。对于同一个数据库实例,在一个连接中可以使用事务操作不同的表。基于以上的两点,我们给 A 增加一个消息表,用来存储需要别的库异步配合的执行消息。在这个例子中,用于存储向 B 发送的消息。那么我们的操作如下

  1. 在 A 中开启事务,对用户执行扣钱 sql,并且往消息表中新增一个消息,消息的内容是要求 B 执行加钱的操作。
  2. 提交事务。如果事务失败则回滚,该业务失败。此时 B 完全无感知。
  3. 如果事务提交成功,则向 B 发出调用,执行增加钱的操作。如果 B 回复成功,将消息表中的该消息删除。
  4. 如果 B 回复失败,则发起重试,或者依靠定时任务不断重试,直到成功。
  5. 成功后删除消息表中的该消息。

我们来分析下上面的做法,首先通过 A 中的事务保证了在 A 上转账的操作落地完成并且有记录可以查询(消息表中的就是未完成的记录)。在事务提交成功后再执行和 B 相关的操作,返回成功才删除消息表,这样就保证操作最后总是可以成功(因为 B 返回了成功消息)。可以看到,在 A 事务成功到 B 调用成功这之间,数据在两个数据库上存在不一致的情况,这也是 BASE 理论中牺牲的强一致性的地方,但是通过这样的做法,数据在两个系统中最终是可以达到一致的,也即是所谓的最终一致性。通过牺牲强一致性,提高了系统的吞吐。并且这个不一致的时间窗口实际上对于一般的用户是无感知的。可能就是在几十毫秒到两三秒之间,用户是可以允许也是理解这样的延迟的。那么上面的方案是否就已经可以解决问题了呢,答案是不。

幂等

在上面的流程可以看到,在 A 事务成功以后要调用 B 的接口,如果调用失败是需要重复调用直到成功的。问题在于,由于需要网络传输调用结果,有可能 B 调用实际上是成功了,但是网络中断导致 A 无法收到消息。那么 A 就会认为是调用失败,从而再次发起调用。那么 B 就将一个加钱的动作执行了 2 次。此时两边的数据处于不一致的状态,并且无法修复。为了解决这个问题,我们引入幂等约束。所谓幂等操作也就是说对于该操作,一次或多次的调用产生的结果是相同的。上面的问题就是由于调用 B 加钱的操作不是幂等,而 A 在理论上必然存在重复调用的情况(因为网络是不可靠的),进而导致数据不一致错误。那么怎么让 B 的加钱操作是幂等的呢?A 中存在一个消息表,用于存放需要执行操作的消息,那么给 B 中也增加一个更新表。对于 A 中的每一个消息都存在一个全局唯一的 id。那么调用 B 的加钱操作的流程修改为如下

  1. B 开启事务。检查更新表中是否存在消息 id。如果存在消息 id,直接忽略该操作。并且返回 A 成功消息。
  2. 如果不存在消息 id,执行用户的加钱操作,并且往更新表中插入该消息。提交事务。提交事务成功返回 A 成功消息,提交失败则返回失败消息。

使用这样的逻辑,则 B 的加钱操作就成为了一个幂等操作,可以承受多次调用。

简单的幂等

上面方案的幂等依靠本地的更新表记录了所有的消息 id 进行比对进而防止多次的重复调用。这样需要一个更新表并且要存储所有的消息,比较重一些。如果我们给于消息一个不断递增的序号,并且 b 的数据表中新增一个序号字段。b 只要执行消息前会比对消息的序号和自身数据的序号。如果消息序号大于自身序号才可以执行。也就是执行如下的伪代码

begin transactionupdate b set money=message.money+b.money,version=message.version where b.id = message.userid and b.version > message.versioncommitend transaction

通过这样的方式,就不需要启用一个单独的更新表。然后对于 B 的业务表有侵入性的修改。

中间总结

在有了上面的例子,现在我们来稍微总结。通过将一致性要求从强一致性降低到最终一致性,我们可以避免 2PC 这样的高成本协议,并且让业务具有更强的伸缩性。将上面具体的方案抽象下,其中的思路还是比较清晰的。

  1. 将一个分布式的事务拆分成多个本地事务,引入消息概念。
  2. 将本地数据更新和消息的新增绑定为一个事务,作为整体进行提交。
  3. 在在一个本地事务成功的情况下,进行下一个远端的事务操作。并且要求该远端事务操作具备幂等性,可以承受重复的多次调用而不会导致数据错误。
  4. 消息可以被本地被多次尝试或者在异步组件中尝试直到消息送达并且操作成功。最终让所有系统的数据达到一致。

上面的思路重点就是在异步消息这个概念的引入。而幂等的保证方式可以有两种,一种是被调用方,也就是接口提供方,自身保存一份执行过的消息表,用于在执行操作前进行比对,避免执行重复操作。一种是将消息引入序号改变,被调用方只执行比自身序号大的消息。

TCC 类型的幂等

上面的基于存储消息方式的幂等由于需要存储执行过的消息会带来额外的存储开销。并且执行过的消息理论上已经失去了其意义(假设调用方执行成功,后续就不会再去调用,那么就没有判重的需求了)。这种方式中,消息的序号是由消息的发送方来生成的,并且被调用方始终需要存储着所有的操作历史,历史数据会越来越多,并且都是无用的历史数据。那我们换个思路,消息的序号是由消息的收取方来生成如何。具体的操作如下

  1. A 开启事务,执行本地业务更新,调用 B 的 try 接口传递业务参数。B 的 try 接口调用成功则返回一个全局唯一的消息 id
  2. A 将这个消息 id 写入到消息表中。提交事务。
  3. 如果事务提交成功,调用 B 的 confirm 接口。接口的参数只有消息 id。表明该业务确认需要执行。B 将真实执行该业务,并且将 confirm 执行结果返回给予 A。如果成功则删除消息表中的该消息。如果失败则通过异步组件发生重试。
  4. 如果事务提交失败,则调用 B 的 cancel 接口,接口参数只有消息 id。表明取消该业务的执行。

在这种方式中,消息的 id 是由被调用方被调用 try 接口时产生。此时被调用方存储该消息 id。在被调用 confirm 或者 cancel 接口时,首先检查该 id 是否存在,存在才执行下一步的操作。对于 cancel 接口而言,操作只是简单的删除该消息即可。而对于 confirm 操作,则需要开启事务,并且在事务中执行对应的操作,并且删除消息。最终提交事务。如果事务成功提交则返回成功消息,否则返回失败。如果消息 id 不存在,有两种可能。

  • 消息 id 是错误的
  • 该消息已经被执行了。

在内部可信的系统内,排除第一种情况,只有第二种情况。故而在这种情况,confirm 接口的调用也是返回成功消息。这种方式,被调用方只需要存储一定规模的消息 id,因为被成功执行的消息都会被删除。再给所有存储的消息一个过期时间。由后台定时组件定期扫描删除即可。这样,消息的堆积大小也是可控的了。

阅读全文: http://gitbook.cn/gitchat/activity/5e02213af60753276dbdd432

您还可以下载 CSDN 旗下精品原创内容社区 GitChat App ,阅读更多 GitChat 专享技术内容哦。

FtooAtPSkEJwnW-9xkCLqSTRpBKX

关注
打赏
1560489824
查看更多评论
立即登录/注册

微信扫码登录

0.0549s