微服务-多个服务副本之间的竞态条件

4
我正在学习微服务-Spring Cloud。在学习过程中,我在思考如何处理以下情况中的竞态条件。
假设我有一个“ProductOrder”服务,它有2个副本。现在有两个订单请求到来,负载均衡器为每个副本分配了一个api请求。请注意,它们共享同一个数据库(因此我不是在谈论数据同步。或者也许我是吗?)。那么服务如何确保存储仍有足够的产品来满足订购请求?
我知道Java中的关键字“synchronized”用于多线程。然而,这些副本不是多线程,而是多进程。我的理解正确吗?有任何想法吗?
1个回答

4
数据库将有一个表,类似于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子句防止其他并发事务在我们的事务运行时修改此产品。将其视为在DB级别而不是JVM级别上同步。
  3. 如果另一个用户尝试购买相同的产品,则该用户的事务(称为T2)将尝试执行相同的操作,第一步是使用锁定查询记录。这将阻止事务T2,直到记录被解除阻止,这将在当前持有锁定的事务提交或回滚时发生。此时,T2将继续进行,并且如果T1更改了产品,则它将看到最新的产品价值。

此方法使用悲观锁,即我们为要更新的记录锁定整个事务时间。

还有另一种方法-乐观。

乐观锁需要在Product表中有一个版本列。然后,第一个查询不会锁定DB中的记录,但我们会得到记录的版本:

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步重新尝试整个操作,即再次读取产品的数量和版本,检查是否有所需数量,并尝试再次更新数量。

当然,我们应该设置一些重试次数限制。对于繁忙的、受欢迎的产品,这可能会成为问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接