处理并发全局“库存”更新的通用策略

Posted

技术标签:

【中文标题】处理并发全局“库存”更新的通用策略【英文标题】:Common strategy in handling concurrent global 'inventory' updates 【发布时间】:2019-09-16 12:21:26 【问题描述】:

举个简单的例子:

我有一个包含一个表的数据库:names,它有 100 万条记录,每条记录都包含一个常见的男孩或女孩的名字,而且每天都会添加更多。

我有一个应用程序服务器,它使用我的网站 'Name Chooser' 将来自父母的 http 请求作为输入。对于每个请求,我需要从数据库中获取一个名称并将其返回,然后不要将该名称提供给另一个父级。服务器是并发的,因此可以处理大量请求,但必须遵守“每个请求的唯一名称”并且仍然具有高可用性。

此用例架构的主要组件和策略是什么?

【问题讨论】:

【参考方案1】:

据我了解,您有两个操作:添加名称选择名称

我有几个问题:

问题 1: 父母选择名字 还是他们也添加名字

问题 2 如果他们添加名称,是否意味着当添加名称时也应该标记为 已经选择了?

假设您不想让所有名称选择请求相互等待(通过锁定排队它们):

仅在选择名称的情况下解决并发问题的一种解决方案是使用Optimistic offline lock。

对此最常见的实现是向表中添加版本字段,并在您将名称标记为选择时增加此版本。您需要为此提供数据库支持,但大多数数据库为此提供了一种机制。 MongoDB 默认为文档添加一个版本字段。对于 RDBMS(如 SQL),您必须自己添加此字段。

您还没有指定您使用的是什么技术,所以我将给出一个使用 SQL DB 的伪代码的示例。对于 MongoDB,您可以检查 DB 如何为您进行这些检查。

NameRecord 
  id,
  name,
  parentID,
  version,
  isChosen,

  function chooseForParent(parentID) 

    if(this.isChosen)
      throw Error/Exception;
    

    this.parentID = parentID
    this.isChosen = true;
    this.version++;
  


NameRecordRepository 

  function getByName(name)  ... 

  function save(record) 

    var oldVersion = record.version - 1;

    var query = "UPDATE records SET ..... 
                 WHERE id = record.id AND version = oldVersion";

    var rowsCount = db.execute(query);

    if(rowsCount == 0) 
      throw ConcurrencyViolation
    
  


// somewhere else in an object or module or whatever...

function chooseName(parentID, name) 

   var record = NameRecordRepository.getByName(name);

   record.chooseForParent(parentID);

   NameRecordRepository.save(record);

在将 whis 对象保存到数据库之前,必须执行版本比较。 SQL 提供了一种基于某些条件执行查询并返回受影响行的行数的方法。在我们的例子中,我们检查数据库中的版本是否与更新前的旧版本相同。如果不是,则表示其他人已更新记录。

在这种简单的情况下,您甚至可以删除版本字段并在 SQL 查询中使用 isChosen 标志,如下所示:

    var query = "UPDATE records SET ..... 
                 WHERE id = record.id AND isChosend = false";

向数据库添加新名称时,您需要一个可以解决并发问题的唯一常量。

【讨论】:

以上是关于处理并发全局“库存”更新的通用策略的主要内容,如果未能解决你的问题,请参考以下文章

数据库存在即更新的高并发处理 - 转

Spring Cloud Gateway 全局通用异常处理

Spring Cloud Gateway 全局通用异常处理

在带弹簧休息的全局异常处理程序中使用通用异常类处理程序是一种好习惯吗?

(随记)php高并发

高并发:通用设计方法