Lec 19 分布式事务
阅读参考书 §9.6
阅读论文:Consistency Rationing in the Cloud: Pay Only When it Matters, vldb09
我们的目标是在不可靠的组件上构建可靠的系统, 并且了解到事务是实现这个目标的抽象,通过提供了原子性和隔离性让我们能够推断出故障。其中原子性通过影子拷贝(性能较低)或者日志(稍微复杂但是性能更佳)实现,而隔离性通过2PL(两阶段锁)来实现。最终,我们还需要将基于事务的系统变成分布式的,即能够运行在跨机器上。
思考题
- 现在我们工作在数据分布在跨多机器上系统上,我们将主要解决什么问题(换句话说,当我们引入了跨多机器后会引入什么新的问题?)
- 如何处理网络重排序问题
- 2PC协议中可能出现的各种故障
Outline
- 两阶段提交
- 论文阅读: 云环境中的一致性配呃
背景引入
如果在跨机器上提交一个事务,示例transfer(A, Z, amout),无故障的情况下流程如下

故障场景
问
问题在于,部分服务器接受到了提交,其他的没有(类似问题也会出现在N-Z服务器崩溃的场景),因此我们的目标是,提出一个协议,能够提供多站点原子性,以应对各种各样的故障(比如,消息丢失、消息重排序、工作者或者协调者故障)
2阶段提交
、
问题: 如果工作节点失败了,有些数据比如账户(A-M),将完全不可用。
问题:需要注意的是,若协调者在准备阶段(prepare phase)发生故障,将导致整个事务阻塞无法推进。此外,与单机运行事务相比,该方案还存在显著更高的延迟
故障类型
- 任何阶段消息丢失: 通过可靠传输,协调者会通过超时机制重发消息。
- 工作者在准备阶段之前故障: 协调者能安全中止事务
- 工作者或协调者在准备阶段期间崩溃:协调者会安全中止事务,会发送显式中止消息给到在线的workers
- 工作者或协调者在提交阶段期间崩溃:协调者不能中止事务,机器必须在恢复期间提交事务。
通信复杂度
两阶段提交协议的通信复杂度。如果一切顺利,涉及 N 个工作站点的两阶段提交协议需要进行3N 次消息传递,如图 9.37 所示:
- 对于每个工作站点,Alice 发送一个 PREPARE 消息。
- 工作站点返回一个 PREPARED 消息。
- Alice 最终发送一个 COMMIT 消息。
两将军问题
假设两支小军队驻扎在一座城市外的两座山上。这座城市防守得当,可以击退并摧毁两支军队中的任何一支。只有两支军队同时进攻,他们才能占领这座城市。因此,指挥军队的两位将军希望协调他们的进攻。两位将军之间唯一的沟通方式是派信使从一个营地到另一个营地。但是,城市的守军在两座山脉之间的山谷中驻扎了哨兵,因此,信使试图越过山谷,却落入敌人手中,无法传递信息。
假设第一位将军发送了这条消息: 发件人:撒旦 收件人:贝吉塔 日期:1 月 11 日,我打算明天黎明越过卢比孔河发动攻击。可以吗?
希望第二位将军会这样回复: 发件人:贝吉塔 收件人:撒旦; 日期:1 月 11 日 ,是的,12 日黎明。 或者,可能是: 发件人:贝吉塔 收件人:撒旦 日期:1 月 11 日 ,不。我正在等待高卢的增援。
进一步假设第一条消息没有成功发送。在这种情况下,第二位将军不会出兵,因为没有收到出兵请求。此外,第一位将军也不会出兵,因为没有收到任何回复,一切正常(除了信使)
现在,假设信使成功传递了第一条消息,而第二位将军发送了“是”的回复,但回复丢失了。第一位将军无法区分这种情况与之前的情况,因此军队不会进军。第二位将军同意进军,但他知道第一位将军不会进军,除非“是”的确认到达,所以第二位将军在不确定第一位将军是否收到确认的情况下不会进军。第二位将军的犹豫表明第一位将军应该发回确认收据: 发件人:撒旦 收件人:贝吉塔 日期:1 月 11 日,木已成舟。
不幸的是,这无济于事,因为携带此确认的信使可能已经丢失,而第二位将军没有收到确认,仍然不会进军。因此陷入困境。现在我们可以直接得出结论:没有一个协议具有有限的消息数量,可以让两位将军相信行军是安全的。如果存在这样的协议,那么该协议的任何特定运行中的最后一条消息对于安全协调来说一定是不必要的,因为它可能会丢失,无法检测到。
他们能做的最好的事情就是接受某个非零的失败概率,该概率等于他们最后一条消息未送达的概率。
为什么两阶段提交无法解决两将军问题?根本区别在于两将军需要“同时”行动,而 2PC 只保证“最终”一致性。两将军问题的严格需求:必须确保双方同时行动,否则就会失败。但如果通信链路延迟,比如一位将军晚收到命令,即使消息最终送达,行动仍会失败。2PC较为松弛,即使某个节点延迟收到“提交”命令,只要最终收到并执行,系统仍能保证一致性。
论文阅读: 云环境中的一致性配额
思考题
- 一致性资源调配(consistency rationing)的目标是什么?
- 一致性资源调配与你在课堂中学到的方法有何不同?
- 你会何时使用一致性资源调配?能否设想一个不适合使用它的场景?
摘要
云存储解决方案承诺提供高可扩展性和低成本。然而,现有的解决方案在所提供的一致性程度上存在差异。我们在使用此类系统的过程中发现,在成本、一致性和可用性之间存在一个非平凡(non-trivial)的权衡。高一致性意味着每次事务的成本较高,并且在某些情况下会导致可用性降低。而低一致性虽然更便宜,但可能会带来更高的运营成本,例如在网上商店中出现商品超卖的情况。
在本文中,我们提出了一种新的事务范式,它不仅允许设计者在数据层面而非事务层面定义一致性保证,还允许在运行时自动切换一致性保证。我们提出了一些技术,使系统能够通过监控数据和/或收集数据的时间统计信息,动态地调整一致性级别。我们通过在 Amazon S3 上实现的第一个原型系统以及运行 TPC-W 基准测试,展示了这些想法的可行性和潜力。我们的实验表明,本文提出的自适应策略显著减少了响应时间和成本,包括因不一致性带来的成本惩罚。
1. 介绍
云存储服务正变得越来越流行,因为它们承诺以低成本提供高度的可扩展性和可用性。这些服务利用由普通硬件组成的计算机集群来提供远程存储功能。现有的商业服务通常将强一致性保证限制在小规模数据集上(例如 Microsoft 的 SQL Data Services),或仅提供最终一致性(例如 Amazon 的 S3)。
如果一个应用程序需要额外的事务性保证,那么这些保证必须构建在云存储解决方案之上。本文关注的是如何在云存储之上实现类似数据库的功能。在这种情况下,强一致性是不可避免的。然而,本研究的一个关键观察是:并非所有数据都需要相同等级的一致性。例如,在一个网络商店中,信用卡信息和账户余额自然需要更高的一致性级别,而用户偏好(如“购买此商品的用户还购买了……”之类的数据)则可以用较低的一致性来处理。这一区别非常重要,因为在云存储环境中,一致性不仅决定正确性,还影响每次事务的实际成本。
在本文中,我们提出一种动态一致性策略来绕过这一难题:在可能的情况下降低一致性要求(即惩罚代价较低时),而在关键时刻提高一致性要求(即惩罚代价过高时)。该策略由一个成本模型和多种指导系统行为的策略驱动。我们称这种方法为“一致性配额”(Consistency Rationing),其名称借用了“库存配额”(Inventory Rationing)的概念。库存配额是一种库存控制策略,根据物品价值的不同以不同精度监控库存。借鉴这一思想,我们将数据分为三类(A、B 和 C),并根据不同的一致性级别对每类数据进行差异化处理。
A 类包含那些一旦发生一致性违规就会导致高额惩罚成本的数据。C 类包含那些可以接受(临时)不一致性的数据(即不存在或只有很低的惩罚成本,或者实际上不会发生真正的不一致)。B 类包含那些一致性需求会随时间变化的数据,例如依赖于某个商品的实时可用性。这类数据通常是由多个用户并发修改的,并且常常成为系统的瓶颈。
本文重点关注的是 B 类数据。正是在这类数据中,我们可以在每次操作的成本和所提供的一致性等级之间做出有意义的权衡。我们将介绍几个使用 B 类数据的动机案例,然后为系统构建一个成本模型,并讨论在不同潜在惩罚成本下动态调整一致性等级的策略。这些策略的目标是最小化基于云存储的总体运营成本。我们基于 Amazon S3 实现的云数据库服务的实验表明,本文提出的动态策略在成本方面具有显著优势。
本文的主要贡献如下:
- 我们引入了一致性配额(Consistency Rationing)的概念,使得应用程序可以以最低可能成本获得所需的一致性等级。
- 我们定义并分析了一些策略,用于在运行时切换一致性协议。实验结果表明,动态调整一致性比静态指定一致性等级具有更好的表现。
- 我们引入了一种基于时间统计的一致性概率保证概念(例如百分位数),适用于数值型和非数值型数据。这类统计性保证在服务等级协议(SLA)中非常重要,据我们所知,这是首次对概率性一致性保证进行详细研究。
- 我们在 Amazon S3 上实现了完整的一致性配额系统,并报告了在不同一致性分类、分类混合以及 B 类策略下,使用 TPC-W 基准测试运行的成本(以美元计)和性能表现。实验结果为运行这类系统的成本结构、每个操作的成本构成,以及如何利用恰当的成本模型来优化这些成本提供了重要的见解。
2. 使用场景
网络商店(Web Shop) 这种网络商店的设计者可以如下使用“一致性配额”策略: (1) 账户信息归为 A 类数据,使用强一致性保证(例如可串行化)访问; (2) 商品库存数据归为 B 类数据。只要库存充足,系统可以容忍一定程度的不一致性;但当库存下降到某个阈值以下时,必须在强一致性保证下访问,以避免超卖; (3) 用户购买偏好和日志信息归为 C 类数据。系统无需随时看到最新状态,也无需采取任何措施确保更新操作的独占性。
拍卖系统 在线拍卖系统的典型特征是:一个拍卖品在拍卖接近尾声时会变得非常热门。竞标者通常在拍卖最后几分钟最为活跃。在这段时间内,拍卖品的当前价格必须是最新的,并在强一致性保证下进行修改。而当某个拍卖品的结束时间还在几天之后时,出价信息可以在较低一致性级别下更新,对系统无任何影响。
3. 相关工作
关于如何在云环境中管理一致性的问题,已经有了不少研究。在这一节中,我们概述一些与本文工作相关的研究方向和成果。
一致性模型
一致性在分布式系统中是一个核心问题。传统数据库系统使用的是强一致性模型,通常基于事务的可串行化(serializability)。相比之下,许多现代的云存储系统提供的是最终一致性(eventual consistency)模型。例如,Amazon 的 Dynamo 和 S3 提供的是一种弱一致性的视图,其中更新最终会传播到所有副本,但在某段时间内,读取操作可能返回旧值。这种模型虽然在性能和可用性上有优势,但对某些需要强保证的应用场景并不适合。
可调一致性
一些系统尝试在强一致性和最终一致性之间提供可调节的一致性(Tunable Consistency)。例如,Cassandra 和 PNUTS 提供了一种允许用户配置读写副本数量的机制,从而在延迟和一致性之间做权衡。Terry 等人提出的 Bayou 系统支持每个数据项定义其一致性语义(session guarantees),并允许冲突的检测和解决。与这些工作不同,我们提出的“一致性配额”(Consistency Rationing)方法不仅允许在系统中为不同数据项定义一致性等级,还引入了动态调整一致性的策略,依据运行时的成本模型和数据使用情况自动调整一致性需求。
分层存储和缓存管理策略
我们的方法也受到分层存储管理策略的启发,例如操作系统中的缓存层或数据库系统中的热/冷数据管理。在这些系统中,资源(如缓存)会根据数据的重要性或访问频率进行优先分配。我们类比这一点,通过将数据分类为 A/B/C 三种类型,并动态决定是否为其提供强一致性。
成本模型与优化
还有一些研究尝试将成本模型引入系统设计中,例如在事务处理或数据复制中的优化策略。我们的方法在这一点上进一步推进:我们定义了详细的成本模型,结合数据类型与一致性违规惩罚成本,使得系统能够在保证业务正确性的前提下优化资源使用。
4. 一致性配额
本节描述了我们提出的一致性配额方法(Consistency Rationing),该方法允许系统以最小成本满足应用对一致性的需求。我们从 ABC 分析模型入手,然后讨论系统如何将不同一致性保证混合使用,最终实现运行时切换协议的能力。
ABC分类法
一致性配额的基础是将数据划分为三类:
- A 类数据(A-data):这类数据对一致性的要求最高,一旦出现不一致就会带来重大代价或风险。例如,银行账户余额或信用卡交易。系统必须确保这些数据在所有节点上保持完全同步,通常使用强一致性协议(如串行化或线性一致性)处理。
- B 类数据(B-data):这类数据的一致性要求是动态的,也就是说,它的正确性与系统状态或上下文有关。例如产品库存或机票预订。当库存充足时,系统可以用较弱的一致性协议处理这些数据,但当库存减少到某个临界点后,就必须切换为强一致性,以避免超卖或重复预订。
- C 类数据(C-data):这类数据即使出现不一致也不会带来严重后果,甚至可以接受一定程度的丢失。例如用户点击日志或商品推荐信息。系统可以用最终一致性或弱一致性协议处理这些数据,从而节省大量成本。
ABC 分类是一种静态划分方案,但它为动态一致性策略的实施提供了基础。我们还允许事务跨多个类别操作,例如一个交易可能同时读写 A、B、C 三类数据。
混合一致性策略
ABC 分类的一个关键特性是:允许不同一致性级别的混合使用。举个例子,某个用户的操作可能会涉及:
- 从 A 类读取账户信息(强一致性);
- 修改 B 类中的库存数据(视库存数量决定一致性策略);
- 写入 C 类的日志记录(弱一致性或异步写入)。
系统通过在事务内部使用不同级别的协议来实现混合一致性策略。为此,事务执行时必须能够识别哪些数据属于哪一类,并选择合适的协议执行子操作。
这种混合策略需要额外的机制来:
- 明确每种数据类型适用的一致性级别;
- 在运行时根据上下文切换协议;
- 解决不同一致性策略交互时可能带来的冲突或异常情况。
一致性与成本模型
为了驱动一致性策略的选择,我们引入了成本模型(cost model)。该模型考虑了两类成本:
- 操作成本(operation cost):即系统执行某种一致性协议所需的实际资源成本。例如,强一致性协议可能需要跨多个副本协调,导致更高的网络延迟和处理开销。
- 违规成本(violation penalty):即由于使用较弱一致性协议导致不一致带来的业务损失。例如:库存错误导致的超卖、客户投诉、退款等。
我们通过估算这两种成本之间的权衡,来判断在某一时刻对某一数据是否需要更强的一致性。简言之:
当违规成本高于额外操作成本时,系统选择强一致性;否则选择弱一致性。
系统可以通过历史记录、访问模式、当前库存或业务策略动态调整这一决策过程。
多类别事务支持
我们的设计允许事务操作跨越多个数据类别。例如,一个订单提交操作可能同时涉及:
- 验证账户余额(A 类);
- 更新库存(B 类);
- 写入购买日志(C 类)。
我们不要求事务内所有操作使用相同的一致性协议,而是每个子操作根据其数据类别采用不同协议。这提升了系统整体的灵活性和效率。
此外,我们也提供了机制来定义子事务或部分一致性保证,例如只对某些关键字段进行原子性控制。
6. 一致性配额在云中的实现
本节描述我们如何在 Amazon S3 之上实现一致性配额框架,并在该系统上运行 TPC-W 基准测试。我们还讨论了系统架构的设计原则以及一致性策略的实现细节。
6.1 系统架构

我们实现了一个中间件组件,它作为云应用与底层存储系统之间的接口,提供以下功能:
- 数据一致性分类(A/B/C 类)
- 动态选择一致性协议
- 成本模型评估与策略调度
- 封装一致性协议(例如:使用锁、版本控制、多副本同步等机制实现串行化)
系统的整体结构如图 1 所示,其中包括:
- 客户端应用:运行 TPC-W 基准测试。
- 一致性代理(Consistency Proxy):核心模块,管理数据分类、策略决策和实际一致性协议执行。
- Amazon S3 存储:作为最终的数据存储层,提供高可用但仅有最终一致性的对象存储。
6.2 协议实现
为了实现在S3桑实现更高级的一致性,基本思路是将页面的更新临时缓存在队列中,如图2的第1步所示。这些在第一步中使用的队列称为 待处理更新队列(PU队列, Pending Update Queues),经过一定时间间隔后,由一个服务器独占地获取该队列的锁,并将 PU 队列中缓存的更新与 S3 上对应的页面进行合并。这一过程被称为检查点操作(checkpointing),如图 2 的第 2 步所示

会话一致性
一种用于实现“读你所写一致性(read-your-writes monotonicity)”的协议。基本思想是,每个服务器会记录过去缓存过的每个页面最高提交时间戳。如果服务器从S3读取到一个比他之前看到的还旧的页面版本,就能识别出这点,并重新从S3读取该页面。
要实现会话一致性,可以通过将同一客户端在会话中的所有请求都重定向到同一台服务器来实现。我们在实现中使用会话 ID 进行路由转发。
此外,在将消息发送到队列之前,还会为消息分配向量时钟(vector clocks),在检查点过程中与完整性约束一起进行校验,从而可以检测冲突并在需要时进行解决。
串行化一致性
为了为 A 类数据提供所需的串行化一致性,我们实现了两阶段锁协议(2PL, Two-Phase Locking)。2PL 对于高冲突场景尤其稳健,因此非常适合那些预期存在较多并发冲突的情况。为了实现 2PL 所需的独占访问控制权,我们实现了一个锁服务。此外,为了确保系统始终能够看到记录的最新视图,我们还实现了一个比 Amazon 的 Simple Queue Service 提供更高保证的高级队列服务。该服务具有以下关键特性:
- 所有消息始终可以被返回;
- 消息的标识符(ID)是单调递增的。
基于这个单调递增的消息 ID,我们能够简化文献 [7] 中提出的单调一致性协议:只需在页面中存储最近一次应用的消息 ID 即可。如果系统重新读取某个页面,发现其消息 ID 比队列中的更新 ID 更旧,就说明该页面需要更新。要将页面更新到最新状态,系统只需从队列中读取所有消息,并仅应用那些 ID 大于当前页面 ID 的消息。
6.3 逻辑日志记录
为了在不引入不一致性的情况下支持更高的并发率,我们实现了逻辑日志记录(logical logging)。一个逻辑日志消息包含被修改记录的 ID 和对该记录执行的操作 Op。当需要获取记录的新状态时(例如在检查点过程中),就从 PU 队列中取出 Op 操作并应用到该数据项上。
[7] 中提出的协议使用的是物理日志记录(physical logging)。而逻辑更新在面对并发更新时更加稳健,只要这些操作是可交换的(commutative),即:
- 更新不会因为被覆盖而丢失;
- 不管执行顺序如何,所有更新最终都会生效。
但如果操作不是可交换的(如乘法、字符串拼接),那就仍可能导致结果不一致。为了避免可交换操作之间的这些问题(比如乘法操作),我们在实现中限制只支持加法和减法。
逻辑日志仅适用于数值类型的数据。对于非数值类型(例如字符串),逻辑日志就退化为物理日志——即“最后一次更新生效”。
我们的一致性配给机制(Consistency Rationing)同时支持这两类数据(详见第 5 节)
6.4 元数据
系统中的每个集合(collection)都包含关于其类型的元数据信息。这部分信息和集合内容一起存储在 S3 上。系统通过查询某条记录所属的集合,从而检查该记录应当遵循哪种一致性保障。
如果一条记录被归类为 A 类数据,系统就知道必须对其应用强一致性保障;相反,如果是 C 类数据,则可以使用较弱的一致性。对于这两类数据(A 和 C),类型检查操作本身就足以决定使用哪种一致性协议。
而对于 B 类数据,元数据中还包含了额外的信息:所采用的策略名称以及相关的参数
6.6 实现的替代方案
假设底层存储服务仅提供最终一致性的保证。要实现更高等级的一致性,需要依赖额外的服务和协议。不过,近年来也出现了一些提供更高一致性保障的系统,例如 Yahoo 的 PNUTS [11]Yahoo PNUTS 提供了一个更高级的 API,支持如下操作:
Read-any:读取任意副本Read-latest:读取最新副本Test-and-set-write(required version):带版本要求的写入测试
利用这些基本操作,可以直接在云服务之上实现会话一致性,而无需额外的协议或队列。虽然其 API 无法实现完整的串行化(serializability),但 Test-and-set-write 足以支持对 A 类数据实施乐观并发控制。我们在本文中提出的元数据结构、统计组件和策略机制也可以直接移植到这个系统中。
遗憾的是,Yahoo PNUTS 并不是一个公开系统,因此我们无法深入研究其实现细节,也未能将其纳入实验范畴。尽管如此,PNUTS 的作者指出,例如 Read-any 操作相比 Read-latest 代价更低。因此,在成本与一致性之间的权衡依然存在,一致性配额(Consistency Rationing)完全可以应用于 PNUTS,以优化其成本和性能。
另一个较新的系统是 Microsoft SQL Data Services [20]。该服务基于 MS SQL Server 构建,并在其基础上实现了复制与负载均衡机制。由于采用的是完整的数据库系统,它可以提供强一致性保障。
不过,该系统有一个关键限制:数据不允许跨多个服务器,事务也不能跨机器执行。因此,这种设计限制了系统的可扩展性。
尽管如此,一致性配额仍可以在 MS SQL Data Services 中应用。特别是 A 类和 C 类数据的分类处理,以及像“边界策略(Demarcation policy)”这类简单策略,有助于提升整体性能。不过,在这种场景下,由于不需要在多台服务器之间交换消息,实现强一致性的代价本身就较低,因此一致性配额节省的成本可能不会像在一个真正分布式、可扩展系统中那样显著。
最后,我们认为一致性配额也非常适合应用于传统的分布式数据库系统,例如数据库集群解决方案。如果系统对一致性要求可以适当放宽,那么一致性配额可以显著减少为保证强一致性而产生的消息通信数量。在这些环境下,我们提出的大多数组件和策略都可以直接应用。
8. 结论
在云计算的存储服务中,每一次服务请求都会带来相应的开销。尤其是一致性协议,它的成本可以被精确地量化为:确保某种一致性级别所需的服务调用次数 × 每次调用的成本。因此,在云存储服务中,一致性不仅影响系统的性能与可用性,还直接决定了整体运营成本。
在本文中,我们提出了一种新概念——一致性配给(Consistency Rationing),用于在面对不一致性会带来惩罚成本的情况下,优化云中数据库系统的运行成本。这种优化的核心思想是:允许数据库在不引起更高惩罚成本的前提下产生某些不一致性,从而降低事务的执行成本。
在我们的方法中,数据被划分为三种一致性类别:A、B 和 C:
- A 类数据必须保证强一致性,因此其每次事务的成本很高;
- C 类数据只保证会话一致性,成本较低,但可能导致不一致;
- B 类数据根据指定的策略,在强一致性与会话一致性之间动态切换。
本文提出并比较了多种用于 B 类数据的切换策略,其中包括提供概率性保证的策略。
正如我们在实验中展示的那样,一致性配给策略显著降低了云数据库系统的总体成本,并提升了性能。实验还表明,基于时间统计信息动态调整一致性协议在维持可接受性能的同时,实现了最优的成本控制。
我们认为,本文中提出的这些基于统计的策略只是实现概率性一致性保证的第一步。未来的研究方向将包括:
- 更好、更快速的统计方法;
- 自动优化其他相关参数(如:能源消耗);
- 在代价函数中加入预算限制;
- 进一步放宽 ACID 原则中的其他维度(例如:持久性)。