BASE理论(译文)

引言

BASE 理论是 eBay 的系统架构师 Dan Pritchett 于 2008 年在 ACM 上发表的论文《Base: An Acid Alternative》将其发扬光大。 BASE 理论引入最终一致性的概念,并系统性地总结了一种针对分布式事务的技术手段(持久性消息队列),其理论价值巨大。这里记录下原文的翻译过程,方便后学者。
经网友纠正,最早提出BASE理论的论文是Cluster-Based Scalable Network Services。但是,也不可否认Dan Pritchett 发表的《Base: An Acid Alternative》将BASE理论的影响力进一步扩大。且该片论文对BASE理论做了专述,其参考价值还是有的。
需要说明的是,BASE理论对比的是ACID理论,不是CAP理论。BASE理论并没有视图打破CAP理论,或者与CAP理论平起平坐。

正文

对跨区域的数据库来说,通过牺牲一定程度的一致性(consistency)来换取可用性(availability),可显著提高系统的可扩展性(scalability,也常被翻译为可伸缩性)。
在过去的十年里,Web应用越来越受欢迎。无论是为最终用户(end users)还是开发人员(如,业务服务)构建应用,作为开发人员都希望所开发的应用可以获得广泛应用。与此同时,随着应用的广泛采用,将带来事务性场景的增长。如果该应用依赖持久化(persistence),那么数据存储将有可能会成为系统的瓶颈。
针对业务增长带来的数据存储瓶颈,有两种扩展应用的策略:垂直扩展(vertical scaling)、水平扩展(horizontal scaling)。垂直扩展就是将应用迁移到更强大的机器上。垂直扩展效果相当不错,但有几个限制。最明显的限制是需求仍会超出系统最大可用容量(随着需求的持续增长,总有一天会超出系统的极限)。另外,垂直扩展价格昂贵,因为增加事务能力通常需要购买一个更大的系统。垂直扩展还会造成供应商锁定(一般大型机仅有少部分厂商提供,如IBM),从而进一步增加成本。
相比垂直扩展,水平扩展可提供更大的灵活性(flexibility),但其实现更复杂。水平扩展可以从两个维度实现:(1) 功能性扩展(functional scaling)、分片扩展(sharding)。功能性扩展包含基于业务功能对数据分组和基于数据库对功能分组。功能性扩展将功能划分到多个数据库。分片则是对同一业务数据进一步划分。图1展示了水平数据扩展的两种策略。
fig1.png
从图1可知,两种水平扩展策略可以同时使用。users、products、trans 三个表可以放在独立的数据库中。此外,每个功能区域都可以跨多个数据库拆分,以实现事务容量(transactional capacity)。如图所示,每个功能区域(functional areas)都可以独立地伸缩(如users、从上图可知,两种水平扩展策略可以同时使用。users、products、trans)。

功能分区(Functional Partitioning)

功能分区对于实现高可扩展性非常重要。任何一个好的数据库体系结构都会将应用拆分为按功能分组的表。上图的users、products、trans等表的拆分就是功能区域的一个示例。利用数据库基本理念(如外键),是保持多个功能区域一致性的通用手段。
通过数据库约束(database constraints)可以保证多个功能分组的一致性。但是这种方式建立了应用与数据库部署策略的耦合。为了保证数据库约束生效,所有的表都必须存储在单个数据库上,从而导致无法随着事务容量的增长进行水平扩展。在大多数情况下,最简单的水平扩展(也称横向扩展,scale-out)方法是将数据的功能分组移动到离散的数据库上。
对于可扩展到高事务量(high transaction volumes)的应用来说,都会将功能不同的数据存储在不同的数据库上。这需要将数据约束(data constraints)移出数据库,并移入到应用。这也引入了本文后面将要解决的几个挑战。

CAP理论

加州大学伯克利分校(UC Berkeley)教授、Inktomi的联合创始人兼首席科学家Eric Brewer, 在 2000 年的 PODC(Priciples of Distributed Computing,分布式计算原理)会议上提出CAP猜想(CAP猜想已被证明正确)。该猜想的具体内容是:对一个Web应用来说,无法同时确保以下所有三个属性(由首字母缩略词 CAP 表示):
一致性(Consistency)。客户端认为一组操作(可以理解成由多个操作组成的事务)总是同时发生。
可用性(Availability)。每个操作都必须在预期的响应中终止。
分区容错性(Partition tolerance)。即使单个组件不可用,操作也可完成。
具体来说,Web 应用最多只能支持其中两个属性。显然,任何水平扩展策略都是基于数据分区(data partitioning)的。因此,设计人员被迫在一致性和可用性之间做出决定(剩余的属性不是舍弃,只是不能很好的支持)。

ACID理论

数据库事务的ACID特性大大简化了应用开发人员的工作量。ACID(由首字母缩略词表示)特性如下:
原子性(Atomicity)。事务是一个原子操作,由一系列动作组成。事务的原子性确保这一系列动作要么全部完成,要么完全不起作用。
一致性(Consistency)。当事务开始和结束时,数据库将处于一致状态。
隔离性(Isolation)。每个事务的行为就好像它是对数据库执行的唯一操作一样。每个事务都感觉不到系统中其他事务在并发地执行。并发事务执行之间无影响,在一个事务内部的操作对其他事务是不产生影响(依赖隔离级别)。
持久性(Durability)。一旦事务完成,操作将不可逆,即使数据库出现系统故障。
数据库供应商(database vendors)很早就认识到对数据库进行分区的必要性,并引入 2PC(两阶段提交)技术,以在多个数据库实例中保证 ACID 特性。该协议可以分为两个阶段:
阶段一:事务协调器(transaction coordinator)要求所涉及的每个数据库预提交操作,并反馈是否可以提交。如果所有数据库都同意提交可以继续,则阶段 2 开始。
阶段二:事务协调器要求每个数据库提交数据。
如果任何数据库否决了提交,则事务协调器会要求所有数据库回滚其事务部分。2PC协议的缺点是什么?对于数据分区的数据库来说,必须保证一致性。根据CAP理论,,那么可用性会受到影响。具体来说,又会如何?
任何系统的可用性都是操作所需组件的可用性的乘积。对系统可用但不是必需的组件,不会降低系统的可用性。在 2PC 提交中涉及两个数据库的事务,其可用性将依赖上述两个数据库的可用性。例如,如果假设每个数据库的可用性为 99.9%(每台机器每月额外停机时间为 43 分钟),则事务的可用性变为 99.8%。计算公式如下:
( 1 − 0.999 ) ∗ 30 ∗ 24 ∗ 60 = 43.8 (1-0.999)*30*24*60=43.8 (10.999)302460=43.8
( 0.999 ∗ 0.999 ) ≈ 0.998 (0.999*0.999)≈0.998 (0.9990.999)0.998

ACID的替代–BASE理论

如果 ACID 为分区数据库提供了一致性选择,那么如何在分区数据中实现可用性呢?一个答案是BASE理论:(Basically Available(基本可用), Soft state(软状态/柔性事务), Eventually consistent(最终一致))。
BASE 理论与 ACID 理论截然相反。ACID是悲观的,在每次操作结束时强制保持一致性。而BASE是乐观的,可接受数据库一致性将处于不断变化的状态。虽然这听起来无法理解,但实际上它非常易于管理,并会带来使用ACID无法获得的可伸缩性。
BASE理论的可用性是通过支持部分失败而非完全失败来实现的。下面是一个简单的示例:如果users表跨五个数据库服务器存储,则根据 BASE 理论,一台数据库服务器故障仅影响到使用该服务器的 20% 的用户。而这,无疑会提高整个系统的可用性。
那么,如果已将应用的数据分解为功能组(functional groups),并对最繁忙的功能组实现了跨数据库的分区(partitioned across multiple databases),那么如何将 BASE 理论应用到程序中呢?BASE 理论要求对逻辑事务中的操作进行比应用于 ACID 的操作更深入的分析。该如何寻找待分析对象?以下各节提供了一些指导。

一致性模式(Consistency Patterns)

按照CAP理论,如果BASE理论允许在分区数据库中提供可用性,那么必须放松一致性的约束。这通常很困难,因为业务利益干系人(business stakeholders)和开发人员(developers)都倾向于相信强一致性对应用的成功至关重要。临时的不一致不能对最终用户隐藏,因此工程师和产品所有者都必须参与放松一致性的设计中。
下图是一个简单的示例,以用来说明 BASE 理论一致性的注意事项。user表包含用户信息,包括买入和卖出的总金额。transaction表包含每笔交易,涉及卖方和买方以及交易金额。以上两个表是对实际情况的简化,但包含说明一致性几个方面的必要字段。
fig2
一般情况下,功能组之间的一致性比功能组内的一致性更容易降级。图2中有两个功能组:user表和transaction表。每次出售商品时,都会向transaction表添加一行,并更新买方和卖方的总金额。使用 ACID 风格的事务,其 SQL 将如图 3 所示。
请添加图片描述
user表中的买入和卖出总金额可看成transaction表的缓存。它的存在是为了提高系统的效率。有鉴于此,可以放宽对其一致性的限制。可以在不影响交易结果的情况下,再买方和卖方的总金额。这并不罕见,事实上,人们经常在交易和实际余额(running balances)之间遇到这种延迟(如,ATM取款和手机通话)。
如何修改 SQL 语句以放宽一致性取决于如何定义实际余额。如果它们只是估计值,则意味着可能会错过某些事务,则修改非常简单,如图 4 所示。
请添加图片描述
现在,我们已将user表和transaction表的更新分离。此时,无法保证user表和transaction表之间的一致性。事实上,第一个和第二个事务之间的失败将导致user表永远无法和transaction表一致,但如果合同规定实际余额(running totals)可以是估计值,那么这也是可以接受的。
但是,如果实际余额的估计值无法解决,该怎么办?如果我们仍然期望将user表和transaction表的更新解耦?引入持久性消息队列(persistent message queue)可以解决此问题。实现持久性消息的选择有很多。但是,实现该队列的最关键因素是确保持久性消息与数据库位于同一资源上。这对允许队列在不使用2PC协议下,按照事务方式提交是必需的。现在,SQL 操作看起来有点不同,如图 5 所示。
请添加图片描述
上述示例在语法上采取了一些自由度,并简化了逻辑,但仍可以帮助说明如何通过引入持久性消息队列来实现user表和transaction表的更新一致性。通过将数据库写入与消息写入持久化队列放在同一个事务,可以获取更新用户实际余额所需的信息(通过消息队列)。该事务包含在单个数据库实例上,因此不会影响系统可用性。
独立的消息处理组件(message-processing component)将从队列中获取消息,并更新到user表。该示例似乎可以解决所有问题,但仍存在一个问题。持久性消息需与执行事务的数据库在同一台主机上,以避免在排队期间出现 2PC。如果消息在包含user表的事务中出队,我们仍然有2PC的情况。(该场景下user表和transaction表分布在不同的主机上)
一个2PC的解决方案是在消息处理组件中执行任何操作。通过将更新操作分离到单独的后端组件中,可以保留面向客户组件(customer-facing component)的可用性。对于业务需求来说,消息处理组件的低可用性是可以接受的。
但是,假设2PC在系统中是绝对不可接受的。那又该如何解决这个问题?首先,需要了解幂等性(idempotence)的概念。如果一个操作执行一次和执行多次都具有相同的结果,则这个操作可称为幂等操作。幂等操作很有用,因为它们允许部分故障。重复执行幂等操作,不会改变系统的最终状态。
参考幂等性后,所选示例会存在问题。更新操作很少是幂等的。该示例在更新user表的买入和卖出列。多次执行该操作显然会导致不正确的用户买入和卖出余额。即使只是简单的设置值的更新操作,但是,在操作顺序方面也不是幂等的。如果系统无法保证按接收顺序来更新数据库,那么系统的最终状态将不正确。稍后将对此进行详细介绍。
对更新的场景,需要一种方法来跟踪哪些更新已完成,哪些更新仍未完成。一种技术是使用记录已完成事务id的表。图 6 展示的表记录了已完成事务id、已更新的金额以及更新金额的用户id。现在,我们的示例伪代码如图 7 所示。
请添加图片描述
请添加图片描述
上述示例取决于能否获取队列中的消息并在成功处理后将其删除。如有必要,可以通过两个独立的事务来完成此操作:一个在消息队列上,一个在user数据库上。除非数据库操作成功提交,否则不会提交消息队列的操作。现在,这个算法可以支持部分故障,且仍然能提供事务保证,而无需诉诸2PC。
剩余唯一需要关心的就是排序。这里有一种更简单的技术可以确保幂等更新。这里调整下示例表结构,以说明幂等更新挑战和对应的解决方案(请参见图 8)。假设还想跟踪用户的最后卖出日期和最后买入日期。这里可以使用上面介绍的方案(借助消息队列更新日期),但有一个问题。
fig8.png
假设在短时间内发生了两次购买,并且消息系统无法确保操作的有序性。那么,将会遇到这样的情况:根据处理消息的顺序,last_purchase的值可能不正确。
幸运的是,这种更新的有序性可以通过对 SQL 进行微小的修改来实现,如图 9 所示。
fig9.png
通过简单的不允许last_purchase时间在时间上倒退,可以使更新操作保证顺序性。还可以使用该方法保护无序更新对更新操作的影响。作为使用时间的替代方法,还可以尝试使用单调递增的事务id。

消息队列顺序性

关于有序消息的传递的简短附注是有必要的(A short side note on ordered message delivery is relevant.)。消息系统提供了确保消息按接收顺序传递的能力。这个特性可能支持起来代价极大,而且通常是不必要的,事实上,有时会给人一种虚假的安全感。
这里提供的示例说明了,如何放宽消息排序并最终仍能提供一致的数据库视图。放宽排序所需的开销是名义上的,并且在大多数情况下明显小于在消息系统中强制保顺的开销。
此外,Web 应用在语义上是一个事件驱动的系统,而与交互风格无关。客户端请求以任意顺序(arbitrary order)到达系统。每个请求所需的处理时间各不相同。系统组件的请求调度是非确定性的,从而会导致消息的非确定性排队。要求保留顺序会给人一种虚假的安全感。简单的事实是,非确定性输入将导致非确定性输出(The simple reality is that nondeterministic inputs will lead to nondeterministic outputs)。

软状态/最终一致性

到目前为止,本文的重点一直是保证交易一致性的同时获得可用性。BASE理论的另一面是理解软状态(soft state)和最终一致性(eventual consistency)对应用设计的影响。
作为软件工程师,我们倾向于将我们的系统视为闭环。(As software engineers we tend to look at our systems as closed loops.)我们从可预测的输入产生可预测的输出的角度来考虑它们行为的可预测性。(We think about the predictability of their behavior in terms of predictable inputs producing predictable outputs.)这是创建正确的软件系统的必要条件。在许多情况下,好消息是,使用BASEl理论不会改变系统的行为的可预测性作为闭环,但它确实需要从整体上查看行为。
一个简单的例子可以帮助我们说明这一点。考虑一个用户可以将资产转移给其他用户的系统。资产的类型无关紧要——它可能是游戏中的金钱或物品。对于这个例子,我们将假设我们已经解耦了两个操作,即从一个用户获取资产,然后使用用于提供解耦的消息队列将其提供给另一个用户。
直觉上,这个系统可能具有不确定性和一定的问题。在一段时间内,资产离开了一个用户,而没有到达另一个用户。这个不一致的时间窗口的大小可由消息系统设计确定。无论如何,开始和结束状态之间存在滞后(在某一段时间内,两个用户似乎都没有资产)。
但是,如果我们从用户视角(user’s perspective)思考这一点,则此滞后可能没有关系,甚至不用知道。资产接收用户和资产发送用户甚至都无需知道资产何时到达。如果发送和接收之间的滞后是几秒钟,那么对于直接就资产转移进行通信的用户来说,这将是不可见的,或者肯定是可以容忍的。在这种情况下,系统行为被认为是一致的,并且用户可接受,即使我们基于软状态和最终一致性实现系统。

事件驱动架构

如果确实需要知道系统状态何时变成一致的,该怎么办?这可能需要使用作用于状态的算法,但仅当系统已达到与传入请求相关的一致状态才可以。简单的方法是依赖于在状态变成一致时生成的事件。
继续思考前面的例子,如果需要通知用户资产已到达,该怎么办?基于提交资产给接收用户的事务创建事件的方式,可以作为一种机制,在系统到达已知状态后执行进一步处理。EDA(事件驱动架构)可在可伸缩性和架构解耦方面提供显著的改进。(EDA (event-driven architecture) can provide dramatic improvements in scalability and architectural decoupling.)关于 EDA 应用的进一步讨论超出了本文的范围。

总结

当系统扩展到需要惊人的事务速率时,需要一种新的方法来权衡管理资源。当负载需要分布在大量组件上时,传统的事务模型暴露出问题。解耦并依次执行这些操作可以提高可用性和伸缩性,但代价是一致性。BASE 理论提供了一个用于考虑这种解耦的模型。

参考

https://queue.acm.org/detail.cfm?id=1394128 Base: An Acid Alternative
https://www.zhihu.com/column/p/29083764 BASE: 替代 ACID
https://cn.bing.com/translator 必应微软翻译
http://highscalability.com/unorthodox-approach-database-design-coming-shard sharding
http://citeseer.ist.psu.edu/544596.html CAP Theorem

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值