【问题标题】:Micro services - race condition between multiple service replica微服务 - 多个服务副本之间的竞争条件
【发布时间】:2021-07-30 08:48:22
【问题描述】:

我现在正在学习微服务-Spring Cloud。在研究的过程中,我在思考如何处理以下情况下的竞态条件。

假设我有一个“ProductOrder”服务,它有 2 个副本。现在有两个订单请求到来,负载均衡器为每个副本分配一个 api 请求。请注意,它们共享同一个数据库(所以我不是在谈论数据同步。或者也许我是?)。那么服务如何确保仓库仍有足够的产品来满足订购要求。

我知道java中的关键字“同步”是用于多线程的。但是,副本不是多线程而是多进程。我对么?有什么想法吗?

【问题讨论】:

    标签: java microservices spring-cloud replication race-condition


    【解决方案1】:

    数据库将有一个类似Product(id, amount) 的表。下订单需要更新该产品的记录。

    它的实现看起来像这样。首先,应用程序将从数据库中读取产品数量:

    SELECT id, amount
    FROM Product
    WHERE id = {some_id}
    FOR UPDATE;
    

    然后应用程序代码将检查是否有请求的金额。然后它会更新数据库中的值:

    UPDATE Product
    SET amount = {new_amount}
    WHERE id = {some_id}
    

    注意:

    1. 所有这些都应该发生在一个数据库事务中
    2. 第一个查询使用FOR UPDATE 子句来防止其他并发事务在我们的事务运行时修改此产品。将其视为synchronized,但不是在 JVM 级别,而是在 DB 级别。
    3. 如果另一个用户将尝试购买相同的产品,该用户的交易(我们称之为 T2)将尝试做同样的事情,第一步是查询带锁的记录。这将阻塞事务 T2 直到记录被解除阻塞,并且当当前持有锁的事务提交或回滚时就会发生这种情况。此时,T2 将继续进行,如果产品被 T1 更改,它将看到产品的最新值。

    这种方法使用悲观锁,即我们在整个事务期间锁定我们想要更新的记录。

    还有另一种方法——乐观。

    乐观锁要求您在 Product 表中有一个版本列。然后第一个查询没有锁定数据库中的记录,但我们得到了记录的版本:

    SELECT id, amount, version
    FROM Product
    WHERE id = {some_id}
    

    签入应用程序的工作方式与悲观情况相同。但最终更新不同:

    UPDATE Product
    SET amount = {new_amount}
    WHERE id = {some_id}
       and version = {version_we_read_initially}
    

    更新操作总是返回更新记录的数量。如果我们得到 0,则意味着在我们的事务运行时记录已更改。这意味着我们最初(在第一个查询中)看到的金额可能已经改变。为了继续,我们需要从第 1 步重试整个操作,即再次读取产品的数量和版本,检查是否有所需的数量并再次尝试更新数量。

    当然,我们要重试的次数应该有一些限制。在需求产品的繁忙系统上,这可能会成为一个问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-11-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多