微服务架构分布式事务管理问题


分布式事务管理问题:如何处理跨多个服务的事务?

微服务是一个分布式系统。一个事务被分发给多个服务,这些服务被顺序或并行调用以完成整个事务。对于微服务架构,最常见的模式是每个微服务的数据库,因此事务也需要跨越不同的数据库。
随着微服务架构的出现,分布式事务管理存在两个关键问题:
子问题

  • 如何维护事务的原子性。原子性意味着事务中的所有步骤都必须成功,或者如果某个步骤失败,则应该回滚之前完成的所有步骤。然而,在微服务架构中,一个事务可以由不同微服务处理的多个本地事务组成。因此,如果本地事务之一失败,如何回滚之前完成的成功事务?
  • 如何管理并发请求的事务隔离级别。事务隔离级别指定事务中的语句可见的数据量,特别是当多个服务调用同时访问同一个数据源时。如果来自任何一个微服务的对象被持久化在数据库中,而另一个请求同时读取同一个对象,服务应该返回旧数据还是新数据?

如何解决这个问题?
在设计基于微服务的应用程序时解决这两个问题至关重要。以下是解决这些问题的两种方法:

  1. 两阶段提交 (2PC)
  2. Saga

两阶段提交 (2PC)
两阶段提交是数据库系统中众所周知的模式。这种模式也可以用于微服务来实现分布式事务。在两阶段提交中,有一个控制节点包含执行操作的大部分逻辑和参与节点(微服务)。它分两个阶段工作:

  • 准备阶段(阶段 1):控制节点询问所有参与节点是否准备好提交。参与节点以是或否响应。
  • 提交阶段(阶段 2):如果所有节点的回答都是肯定的,则控制节点要求它们提交。即使一个节点的回答是否定的,控制节点也会要求它们回滚。

尽管 2PC 可以帮助在分布式系统中提供事务管理,但它也成为单点故障,因为事务的责任落在了协调者身上。随着阶段的数量,整体性能也会受到影响。由于协调器的健谈,整个系统受到最慢资源的约束,因为任何就绪节点都必须等待来自较慢节点的确认。此外,这种协调器的典型实现本质上是同步的,这可能导致未来吞吐量降低。2PC还是有以下缺点:

  • 如果一个微服务在提交阶段变得不可用,则没有机制可以回滚另一个事务。
  • 其他服务必须等到最慢的服务完成确认。服务使用的资源被锁定,直到整个事务完成。
  • 由于它们依赖于事务协调器,两阶段提交在设计上很慢。这可能会导致可伸缩性问题,尤其是在基于微服务的应用程序和涉及许多服务的回滚场景中。

Saga
正如 Hector Garcia-Molina 和 Kenneth Salem 在其 1987 年计算机协会文章中所描述的那样,Saga是执行特定工作单元的一系列操作,并且通常相互交错。作为 Saga 一部分的每个操作都可以通过补偿操作回滚。Saga 保证所有操作成功完成,或者为所有执行的操作运行相应的补偿操作以回滚之前完成的任何工作。

补偿动作必须是幂等的,并且必须能够重试直到成功执行,本质上使它成为一个不会失败的动作,并且不需要人工干预来解决它的失败。Saga 执行协调器 (SEC) 为整个流程提供了保证和能力,使其成为成功或成功中止并具有必要回滚的事务。
Saga 模式在分布式事务场景中有何帮助?

微服务引入了另一组管理事务的问题,因为每个域驱动的服务都是单独部署并隔离运行的。使用微服务架构,单个业务流程将多个微服务组合在一起以提供整体解决方案。使用微服务架构实现 ACID(原子性、一致性、隔离性、持久性)事务非常困难,并且在某些情况下是不可能的。例如,在上述电子商务示例中,具有优惠券功能的微服务无法获取支付数据库的锁定,因为它在大多数情况下是外部服务。但是仍然需要某种形式的事务管理,所以这些事务被称为BASE事务:基本可用性、软状态和最终一致性。必须采取补偿措施来恢复作为交易的一部分发生的任何事情。

这是 Saga 模式完美契合的地方,因为它有助于:

  • 跨多个微服务保持数​​据一致性,无需紧密耦合。
  • 与 2PC 相比,性能更好。
  • 不提供单点故障。
  • 保持事务的整体状态最终一致

实现 Saga 模式的不同方法
实现 Saga 模式有两种合乎逻辑的方式:编舞Choreography和编排orchestration。

编舞Choreography
在 Saga 编舞模式中,作为流程一部分的每个单独的微服务都会发布一个事件,该事件由后续的微服务接收。您必须在微服务开发生命周期的早期做出决定,以了解它是否会成为 Saga 模式的一部分,因为您必须选择一个合适的框架来帮助实现该模式。要采用特定的框架代码,必须使用注释、类初始化或其他配置更改来装饰微服务。在 Saga 编舞模式中,SEC 可以嵌入到微服务中,或者在大多数场景中是一个独立的组件


每当服务出现时,它都会向 SEC 注册,这使其成为可能跨越各种微服务的交易的一部分。SEC 在其日志中维护事件的顺序,这有助于它决定在发生故障时调用的补偿服务和顺序。

当参与分布式事务的微服务数量在 2 到 4 个之间时,首选编舞实现。在超过 4 个服务的情况下,应用编排实现更合适。
Saga 编舞模式是您开始微服务之旅(本质上是全新开发)并了解有必要适时引入流程微服务的理想选择。
编舞的缺点

  • 很难跟踪哪个服务正在侦听哪个队列。添加新服务可能既困难又令人困惑。
  • 服务之间存在循环依赖的风险,因为它们会消耗彼此的队列。
  • 集成测试很困难,因为必须运行所有服务才能模拟流程。

编排orchestration
正如 Saga 编排模式的名称所暗示的那样,有一个编排器组件负责管理整个流程。如果进程在调用任何单个微服务时遇到错误,那么它也负责调用补偿服务。编排器帮助对 Saga 流进行建模,但也依赖底层框架按顺序调用服务,并在任何服务失败时进行补偿调用。

编排的优势

  • 它非常适合存在许多服务并且随着时间的推移添加服务的复杂工作流程。这里,下限是 4 个服务。如果服务超过 4 个,则优先使用编排实现
  • 它提供对每个服务及其活动的集中控制。
  • 由于编排实现单方面依赖于 Saga 参与者(服务),因此不存在循环依赖。
  • 并非每个服务都需要了解其他服务的任何信息!因此,存在关注点分离。
  • 它比编舞实现更容易实现和测试。
  • 由于完成的工作将保持线性,撤消或补偿管理更容易。

编排实现的唯一缺点是整个工作流程由 Saga Orchestrator 管理(单点风险)。
因此
  • 可以看出,无论在 Saga 中使用哪种实现,无论是编排还是编排,复杂性都会随着服务数量的增加而增加。
  • 在 Saga 中进行调试是一项相当困难的操作。
  • 在 Saga 服务的本地数据库中所做的任何更改都无法撤消!
  • 如果一个 Saga 处理数据而不读取另一个 Saga 所做的更改,则可能会发生丢失更新。
  • 或者,当一个服务在完成更新之前读取另一个服务所做的更新时,可能会发生脏读。

由此产生的分布式事务问题得到了解决,但它的实现必须小心。

可以查看开源示例 saga 项目