【问题标题】:Micro Services and Transaction Manager how to handle concurrency Issue微服务和事务管理器如何处理并发问题
【发布时间】:2019-01-14 22:34:28
【问题描述】:

我正在构建一个使用基于 Java Transaction API(JTA) 实现的事务管理器的微服务。

我的问题是 Trasaction maanger 是否有能力处理分布式数据库场景中的并发问题。

场景:

假设有多个服务实例正在运行,我们收到两个请求,将帐户中的余额金额更新 10。最初,一个帐户可以有 100 美元,第一个实例得到它并将其增加到 10 美元,但尚未提交。

同时,第二个实例还检索仍然为 100 的帐户并将其增加 10 美元,然后将其提交更新余额为 110 美元,然后服务一个再次将帐户更新为 110 美元。

到这个时候你一定已经知道余额应该增加 20 美元而不是 10 美元。我是否必须编写某种乐观锁异常机制来防止上述情况,或者基于 JTA 规范的事务管理器是否已经确保这种事情不会发生吧?

【问题讨论】:

  • 为什么要存储总金额?为什么不存储交易并总结。 Si 当前余额为 100。服务调用 1 添加新事务 10,第二个也添加 10。余额显示所有交易的总和并显示120。不需要一些全局交易处理。
  • 这是一个典型的例子,你必须处理乐观锁定。它与微服务无关,也没有分布式事务。也许这会对你有所帮助...baeldung.com/jpa-optimistic-locking

标签: java concurrency microservices distributed-transactions optimistic-locking


【解决方案1】:

事务管理器是否有能力处理分布式数据库场景中的并发问题。

事务和并发是两个独立的概念,虽然事务在我们也看到并发的上下文中变得最重要,但事务在没有并发的情况下也很重要。

回答您的问题:不,事务管理器通常不关心处理并发更新引起的问题。它采用一种非常幼稚和简单(通常也是最有意义)的方法:如果在事务开始后,它检测到状态变得不一致(因为并发更新),它会将其简单地引发为异常并回滚事务。如果它能够确定事务的 ACID 属性的所有条件仍然有效,它将提交事务。

【讨论】:

    【解决方案2】:

    对于这种类型的请求,您可以通过 Optimistic Concurrency 处理,您可以在数据库中有一列(时间戳)作为对版本号的引用。

    每次提交更改都会修改时间戳值。

    如果两个请求尝试同时提交更改,则只有其中一个会成功,因为版本(时间戳)列将更改,然后否定其他请求提交更改。

    【讨论】:

    • 假设事务管理器不会处理我上面描述的场景是否正确?
    • 我不太确定 JTA 是如何工作的,但是如果您可以将 Optimistic Concurrency 与 JTA 结合使用,它可以在发生异常时回滚您的更改(版本号已因另一个请求而更改)
    【解决方案3】:

    事务管理器(作为 JTA 规范的实现)使在多个资源之上的工作变得透明。它确保所有操作都作为单个工作单元进行。 “在多个资源之上工作”意味着应用程序可以将数据插入数据库,同时向 JMS 代理发送消息。事务管理器保证为这两个操作保留 ACID 属性。以简单的形式,当事务成功完成时,应用程序开发人员可以确定两个操作都已处理。当一些麻烦发生时,事务管理器会处理它——可能会抛出异常并回滚数据更改。因此,没有处理任何操作。 对于不需要关心更新第一个数据库和 JMS 并检查所有数据更改是否真正处理或是否发生故障的应用程序开发人员来说,这使这一点变得透明。

    一般来说,JTA 规范在编写时并未考虑到微服务架构。现在这真的取决于您的系统设计(!)但是如果我认为您有两个微服务,每个微服务都附加了自己的事务管理器,那么事务管理器无法帮助您解决并发问题。事务管理器(通常)在某些同步中不起作用。您不会使用来自一个微服务的多个资源(事务管理器的用例是什么),而是使用来自多个微服务的一个资源。

    由于只有一个资源,因此它是您所有更新的同步点。这取决于它如何管理并发。考虑到它是一个 SQL 数据库,那么它取决于它使用的隔离级别(ACID - I = 隔离,请参阅https://en.wikipedia.org/wiki/ACID_(computer_science))。您的特定示例讨论了丢失更新现象(https://vladmihalcea.com/a-beginners-guide-to-database-locking-and-the-lost-update-phenomena/)。因为两个微服务都试图更新一条记录。避免该问题的一种解决方案是使用乐观/悲观锁定(您可以通过例如上述时间戳自己实现它),另一种是在数据库中使用可序列化隔离级别,或者您可以将应用程序设计为不读取并根据第一次读取的内容更新数据,但更改具有更新原子的 sql 查询(或者可能有其他策略如何使用您的数据模型来实现所需的结果)。

    总之——这取决于你的事务管理器是如何实现的,它可以在某种程度上帮助你,但这不是它的目的。您的目标应该是检查如何在共享存储中设置隔离级别,并考虑您的应用程序是否需要在应用程序级别处理丢失更新现象,或者您的存储可以为您管理它。

    【讨论】:

      猜你喜欢
      • 2020-02-20
      • 2021-11-18
      • 1970-01-01
      • 2020-05-28
      • 1970-01-01
      • 2017-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多