牺牲一致性来换取分布式架构的可伸缩性

  • Floyd Marinescu
  • Charles Humble
  • 王丽娟

2008 年 3 月 11 日

话题:架构

系统架构师角色关键的一方面就是衡量相互冲突的需求、决定解决方案,常常要牺牲一个方面来换取另一个方面。随着系统变得越来越大、越来越复杂,越来越多关于如何构建应用的传统智慧正在受到挑战。比如说,去年 3 月在伦敦召开的 QCon 会议上,Dan Pritchard 谈论了 eBay 的架构。他的介绍随后得到了很多的报道,其中一个主要的结论就是 eBay 不使用事务,用数据一致性上的损失来换取系统整体伸缩性和性能上相当大的改进。

InfoQ 接着 Dan Pritchard 在 QCon 会议上的谈话与他继续讨论,以获得更多信息:

为什么 eBay 不使用事务,或者为什么可以决定不采取应用级事务?

我们并非一概不使用事务。我们只是不使用跨物理资源的事务,因为它会造成多个组件之间出现依赖。组件可以是应用服务器和数据库。(例如在客户端控制的事务中,)一个客户端的失败会长久地阻塞数据库资源、超出我们的忍受程度。我们也不使用分布式事务,因为让应用依赖于多个数据库会降低客户端实际的可用性。相反,我们选择缺少事务的设计,并加入失效模式,失效模式可以使客户端甚至在发生数据库可用性问题的时候也能继续进行。

应用级事务总是有些问题。只要让开发人员管理资源的生命周期,就少不了因管理出错而引起的 Bug。事务管理和内存管理比起来没有多大的不同,而且我们看到由于生命周期问题,语言的总体趋势是不再让开发人员负责内存管理。假设 Bean 后面的每个数据库操作都是同等重要的,那么声明性事务(就像 EJB 中的那些)就是一个简化事务管理的强有力的方法。

是否采用事务真正取决于你的伸缩性和可用性目标。如果你的应用需要达到每秒数百笔事务,你会发现分布式事务达不到这一目标。如果你想使可用性超过 99.9%,那么你根本不能想当然地假设所有的数据库提交都能在 Web 页面的上下文中完成。遗憾的是,对于何时应当放弃应用级事务并没有简单的规则。相反,做为一名架构师,你必须决定什么时候应当为了满足系统的一个制约因素的要求而放松对另一个制约因素的要求。

你是怎样为像“出价竞拍”这样的操作实现原子性的?

出价竞拍本身就是一个很有意思的问题,原子性并不是重点,更多的是关系到在拍卖关键的最后几秒钟里不要阻塞任何出价人。如果改成在显示时刻而不是在出价时刻计算最高出价人和最高出价,就会变得非常简单。所有出价都被插入到一个单独的子表,插入操作不太会引起资源争用的情况。每次显示产品的时候,再重新取回所有的出价,并且在这个时候应用业务逻辑来决定最高的出价人。

你的问题背后隐藏的真正问题是我们如何实现一致性?要在大型系统中实现一致性,你必须放弃 ACID,转而使用 BASE:

基本可用(Basically Available)

软状态(Soft state)

最终一致(Eventually consistent)

如果你能够在每个客户端请求快结束的时候放松对数据一致的要求,就有可能消除分布式事务,并使用其它机制来达成一致的状态。举例来说,在上面的出价案例中,我们也更新视图数据表,视图数据表是按照出价人来组织数据的,目的是加速“我的 eBay”页面的显示。这里用两个异步事件来完成。一个是依靠内存中的队列,因为我们希望尽量缩短从出价到在显示在“我的 eBay”页面上之间的响应时间。但是,内存中的队列不可靠,所以在发生出价操作的时候,我们同时用一个服务器端事务来捕获出价事件。即使内存中队列的操作失败了,这个出价事件也能根据还原机制被处理。出价人视图数据表因此而解耦,但不总是与出价表的状态保持一致。不过这是我们可以接受的让步,它让出价表和出价视图表之间不必服从 ACID 要求。

对其它大型系统的架构,你有什么建议吗?

最简单的建议就是,给一个为小规模应用而设计的架构增加资源并不能让它变成大规模的架构。你必须打破常规模式,比如 ACID 和分布式事务。乐于寻找机会放松一些约束,即使传统上认为是不能放松的。

还有两条简单的原则:把每样东西都设计成分离的;考虑 BASE、而不是 ACID。

亚马逊 CTO Werner Vogels也在 QCon发了言,他通过引用 Eric Brewer 的 CAP 定理提供了一些权衡取舍更深层的背景。这个定理曾在2000 年 PODC 会议上(.pdf 文件)进行过介绍,介绍中也包括 ACID vs. BASE 的内容。它陈述了对于数据共享系统的三项属性——数据一致性、系统可用性、对网络分区的耐受性——在同一时间只能达成其中的两项。换句话说,一个不能容忍网络分区的系统可以利用像事务这样普通的技术来实现一致性和可用性。然而,像亚马逊和 eBay 这样的大型分布式系统,网络分区是既定的。它的后果就是,大型分布式系统的架构必须决定时放松对一致性的要求,还是放松对可用性的要求。两种选择都会给开发人员造成一些负担,他们需要了解他们处理的架构的特点。比如说,如果你选择放松一致性要求,那么开发人员就要决定怎样处理这种情形——对系统的写入不会立即反映到对应的读出中。就像 Windows Live 项目经理 Dare Obasanjo 在他的博客中写的一样。

我们在 Windows Live 平台的某些方面也采用了类似的做法。我也听到了开发人员抱怨一件事情,就是原先能通过事务轻松获得的错误恢复,现在要留给应用开发人员来处理。最大的苦恼往往是关于回滚复杂的批处理操作。

许多大型网站似乎都殊途同归,得到了同样的结论。观察到这一点是很有意思的。虽然只有几个节点的小型系统尚不需要关注这些形形色色的权衡取舍,但是 eBay 和亚马逊正在处理的各种问题可能已经开始在企业系统中出现了,因为这些企业系统的用户规模也正变得越来越大。

查看英文原文:Trading Consistency for Scalability in Distributed Architectures

架构