两阶段提交协议的 ACID 是怎样的?
Posted
技术标签:
【中文标题】两阶段提交协议的 ACID 是怎样的?【英文标题】:How ACID is the two-phase commit protocol? 【发布时间】:2011-06-06 02:24:42 【问题描述】:我遇到了一种情况,我开始怀疑两阶段提交协议是否真的能保证 ACID 属性,尤其是它的“A”部分。
让我们看一个涉及 2 个资源的理论上的分布式事务。 (关于我必须处理的问题的更实用的描述可以在my blog 中找到)。该场景是分布式事务的正常执行(无故障或恢复)。应用程序启动事务,更新资源并发出 commit() 调用。提交完成后,应用程序检查两个资源并查看已完成事务的所有更改。一切都很好,2PC 协议完成了它的工作,对吧?
现在,对场景进行一个小改动。当分布式事务执行 commit() 时,另一个应用程序将访问相同的 2 个资源。它只能看到交易的部分变化吗?比方说,对一个资源的更改已经可见,而对第二个资源的更改尚不可见?
在我阅读的有关 2PC 协议的所有信息中,我找不到任何关于各个资源相对于彼此的变化的可见性的保证。而且我找不到任何说明所有资源都在完全相同的时间完成它们各自的提交的东西。
【问题讨论】:
据我所知,2PC 从不承诺 Atomic,而且它不能。要获得 Atomic,读取过程必须关注部分提交的资源。任何人读取资源并发现它处于准备状态,它必须引用任何单个点(存储 Commit 事件的位置)来检查该资源的真实状态(准备或提交)。 【参考方案1】:我认为您的主题令人困惑。 2PC 确保事务提交具有一定的可见性。 IE。在您的事务中,您提交的数据将以特定方式排序,并且与该事务一起提交的数据将按顺序提交。
在事务之外,您看到的行为将取决于数据库中锁定的工作方式。通常,您会期望只读查询会看到之前的事务状态或之后的事务状态(除非它使用相同的锁定语义,否则不能保证它接收到的本身)。写入语义通常会导致锁定事务中的所有项目,但这实际上取决于您的数据库的配置方式。
2PC 实际上只承诺一个操作是原子的,即使那样它也只是在该事务的范围内必须是原子的,具体取决于您的数据库的配置方式。
【讨论】:
【参考方案2】:我认为您将 2PC 与 concurrency control 混淆了。两阶段提交确保事务中的所有参与线程要么提交要么中止。并发控制确保在相同或不同应用程序中对事务进行某种排序。根据您的要求,有多种处理方式,但完全序列化的事务当然是可能的。
【讨论】:
我不会将 2PC 与并发控制混淆。当我依靠交易基础设施来正确地做这件事时,我并没有想到它。事实是: 1. 如果我使用的是非分布式事务,我不必担心这一点。 (如果这样的事务更改了数据库中的多个表,则与第一个事务并行运行的其他应用程序不会看到对一个表的更改,但对另一个表没有更改。) 2. 我什至可能不知道我在分布式事务中运行. 3.即使我知道:已经有一个繁重的基础设施来确保一些基本的事务属性。为什么我需要在它旁边发明我自己的来真的-真的-真的确保它? 4. 在阅读了 [the 2PC][1] 和关于 atomic commit 的链接文章后,我的理解是:我可以放心地期待 2PC 的“全有或全无”语义。 [1]:en.wikipedia.org/wiki/Two_phase_commit 您说“如果这样的 [非分布式] 事务更改了数据库中的多个表,则与提交的第一个事务并行运行的其他应用程序不会看到对一个表的更改,但对其他表没有更改” - 但这是不是正确的。如果你有一个 REPEATABLE READ 隔离级别,并且事务 A 从表 X 读取,然后事务 B 写入表 X 和 Y,然后事务 A 重新从 X 读取并从 Y 读取,它将看到来自 X 的旧数据,并且Y 的新数据。 假设这个场景: 1. 首先我们有a=0,b=0; 2.一个交易要设置a=1,设置b=2; 3. 使用2PC,a和b都处于prepared
状态,然后将提交点设置为committed
状态,然后将a更新为committed
状态,同时,b仍处于prepared
状态。 4.现在我们读取a,发现a=1,之后,读取b,发现b=0。问题是,这种行为是不是违反了原子性?在确认 a=1 后,我希望 b=2。
否则,原子性实际上是最终原子性。【参考方案3】:
来自***
The article 在早期明确指出 2PC 无法适应所有可能的故障配置。更具体的参考这里:Three phase commit protocol:
3PC 最初由 Dale Skeen 和 Michael Stonebraker 在他们的论文“分布式系统中的崩溃恢复的正式模型”中描述。[1]在那项工作中,他们将 2PC 建模为一个非确定性有限状态自动机系统,并证明它对随机单站点故障没有弹性。
编辑:这可用于破坏所有 4 个 ACID 属性。我最初的答案是错误的。
更改的可见性
是一个正交问题,取决于所涉及的同类群组配置(请参阅Isolation levels)。
您的问题和blog 的主要内容是关于隔离,而不是原子性。原子性意味着事务始终完成到最后,最终提交所有更改(或全部回滚)。
即使所有同类群组都可序列化并使用相同的 TransactionManager,我仍然看到通过在正确的时间启动新事务来规避隔离的可能性。
研究了你的博客,我想说,两阶段提交协议不能解决你的问题,MDB通信必须重新设计更有创意。
个人经验
由于阻塞特性,2PC 几乎在任何实际情况下都是过度杀伤力,而明智地设计存储位置和数据流可以提供更好的解决方案。基本上,您需要任何类型信息的唯一最权威来源,并且所有其他相关方必须能够将自己更新到该权威状态。 http://en.wikipedia.org/wiki/Communicating_sequential_processes 是一个很好的灵感,尤其是在 Go 中实现的。现代分布式数据库是 BASE 而不是 ACID (http://en.wikipedia.org/wiki/Eventual_consistency)。
【讨论】:
以上是关于两阶段提交协议的 ACID 是怎样的?的主要内容,如果未能解决你的问题,请参考以下文章