【问题标题】:How does Two Phase Commit really work at low level?两阶段提交如何真正在低级别工作?
【发布时间】:2021-07-10 04:58:33
【问题描述】:

互联网上有大量关于 2 阶段提交的文章。 他们都在说同样的话,我不明白。我需要对它有一个低层次的了解。

订单和支付服务示例是互联网上最流行的示例。

假设我们有订单服务和付款服务。下订单后,Order 服务将其写入其数据库,但支付服务也必须将其写入自己的数据库才能完成交易。

这是我的理解不足:

  1. 用户向 Orchestrator 发送下订单请求

  2. Orchestrator 同时调用 Orders 服务和 Payment 服务。现在根据我所阅读的内容,订单和支付服务应该通过告诉它是否准备好来响应 Orchestrator。 这是什么意思?在这里做好准备是什么意思?

  3. 订单和支付服务做出响应,告诉 Orchestrator 他们已经“准备就绪”(不管是什么意思)。

  4. Orchestrator 向这两个服务发送另一个请求(提交请求)。

  5. Order 将记录写入其数据库。支付服务将记录写入自己的数据库。它们都以状态 200 响应 Orchestrator。

  6. Orchestrator 检查两个参与者是否都返回了状态代码 200。如果是,则它什么也不做。如果不是,那么它会要求他们中止? 怎么样?其中一位参与者已经将交易写入其数据库。

【问题讨论】:

  • 你的理解有缺陷;没有任何事情发生的第一步,只有第二步是不可逆转的。第一步包括参与者试探性地执行请求并报告“我做到了”,第二步是协调者根据是否每个人都做了“完成”或“回滚”命令。是的,事务必须能够在发出最终“提交”信号之前的任何时间中止(回滚);这可以通过多种方式实现。 WP 有一个decent article
  • 请注意,数据库(至少是关系型数据库,还有大多数其他类型的数据库)已经自己实现了事务。对于一个服务,它的实现变得微不足道:它所要做的就是启动一个数据库事务,执行请求,返回报告,然后按照指示回滚或提交它。如何实现回滚不需要关心它;那是数据库的问题。 (通常,数据库将原始数据的副本与事务一起存储在日志中,因此回滚包括写回该副本。)
  • @JeroenMostert 第一步“用户向 Orchestrator 发送下订单请求”是用户单击 UI 上的按钮。我知道这不是 2pc 的一部分。当您说第一步由参与者试探性地执行请求并报告“我做到了”时,您是什么意思。第一步如何?第一步不是编排器向参与者发送请求吗?
  • 是的,但是请求也会立即执行(带有回滚选项)。除了 Wikipedia 文章之外,我不确定您在寻找什么,我认为这很好地解释了它。基本上,编排器要求每个人做他们应该做的事情并报告成功或失败,然后当所有节点都报告回来时,它会要求所有节点根据集体结果继续提交或回滚(如果全部提交,则提交)节点报告成功,如果至少有一个报告失败则回滚)。
  • @JeroenMostert 我在互联网上找到的维基百科或任何其他文章都非常抽象。当然,根据定义,模式是抽象的。但是我需要诸如以下的详细信息:如果对于库存管理项目,您有 5 个微服务。这是否意味着如果需要,他们都可以在某个时候成为协调员服务?还是必须是其他服务(第 6 项服务),其作用是协调库存项目中的所有事务?如果编排器本身失败了怎么办?

标签: sql .net algorithm design-patterns microservices


【解决方案1】:

两阶段提交都是关于处理失败及其含义。

在第 1 阶段,编排器告诉订单和支付服务准备提交。如果一切顺利,他们都会回复“准备好”,这意味着:

  • 事务被持久记录,并标记为准备
  • 除了某些其他服务准备失败之外,它不可能由于冲突或任何其他原因而需要回滚。事务准备好后,只有编排器才能正常回滚。

如果订单和支付处理器都成功准备,那么编排器将告诉他们完成提交。最终确定的意思:

  • 事务被持久记录并标记为已提交
  • 无法回滚。

如果在此过程中出现任何问题,可以检查支付和订单服务中交易的持久记录状态,以确定它是否“真的发生”以及如何恢复:

  • 如果不是所有服务都准备好,那么事务就没有发生。编排器将在所有准备它的服务中回滚事务。如果出现问题,这可能需要人工干预,否则编排器可能无法完成此操作,直到事情恢复正常。
  • 如果所有服务都成功准备,那么事务确实发生了。编排器将告诉所有尚未完成的服务继续执行此操作。同样,这可能必须等到已关闭的系统重新启动。

此外,有时恢复不是协调者的工作。如果编排器放弃,那么各个服务可以相互检查事务是否发生。

重要的一点是,一旦两阶段提交开始,无论发生什么,您都可以通过检查持久事务记录将系统恢复到一致状态。

在实践中,两阶段提交并不经常使用,因为当事务处于准备但未完成状态时,任何其他使用其数据的事务本身不能提交,因为它们可能需要回滚.这种事务需要相互等待的争用会减慢整个系统的速度。

【讨论】:

  • 这是一个很好的答案。谢谢你。我现在明白了。
【解决方案2】:

我将向您解释成功购买的步骤。客户注册订单,然后到支付网关,在那里支付,然后返回商店网站,支付记录被记录,操作员可以跟踪步骤。

订单表

Id int,
CustomerName nvarchar(150),
TotalPrice int,
SendState tinyint,
InsertDate datetime

付款表

Id int,
OrderID int,
PayState tiniyint,
PayPrice int

客户现在在数据库中注册订单

Insert into Order values(1,'bill',30000,1,'5/1/2021 8:30:52 AM')

现在客户连接到支付网关并支付成功,商店网站返回。

Insert into Payment values(1,1,200,30000)

现在为操作员显示结果

select o.*,p.PayState,p.PayPrice 
from Order o join Payment p on o.Id = p.OrderId

如果您不付款,则会在数据库中记录500的错误,操作员看到500的状态就会知道付款不成功。

Insert into Payment values(1,1,500,0)

【讨论】:

  • 这无法解释两阶段提交的工作原理。如果付款失败会怎样?请注意,该问题预设了单独的服务,这些服务可能不使用相同的数据库,或者根本不使用任何数据库。
  • 假设如果你不付款,会在数据库中注册一个错误500,操作员看到500的状态就知道付款不成功。
  • 谁是“操作员”?一个人?一个软件?如果付款失败,订单会怎样?两阶段提交意味着所有步骤都成功,或者所有操作都被还原,因此整体状态是一致的。您在这里描述的只是快乐的流程,根本不处理任何失败。
  • 销售人员审核订单,仅认为注册了 200 消息的订单是成功的。回滚交易并不理想地存储在数据库中,以便客户稍后完成支付操作
  • 您所描述的原则上是构建订单系统的好方法(实际上您确实不想回滚任何东西),但不幸的是它与原始问题无关,它预设了一个对事务使用两阶段提交的系统,而您的实现却没有。这个例子只是一个例子——OP 不是在寻找关于如何构建这样一个系统的建议,而是在寻找关于两阶段提交如何工作的解释。
猜你喜欢
  • 2014-09-12
  • 2011-11-15
  • 1970-01-01
  • 2011-11-24
  • 2013-05-21
  • 2018-04-17
  • 2015-02-02
  • 1970-01-01
相关资源
最近更新 更多