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

时间:2019-04-27 03:00:20

标签: database architecture scalability

举一个简单的例子:

我有一个只有一个表的数据库:名称,其中有100万条记录,每个记录包含一个普通的男孩或女孩的名字,并且每天都有更多记录。

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

此用例的体系结构的主要组成部分和策略是什么?

1 个答案:

答案 0 :(得分:1)

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

我有几个问题:

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

  • 问题2 如果它们添加名称,这并不意味着当添加名称时,它也应标记为< em>已经选择?

假设您不想让所有名称选择请求都彼此等待(通过锁定它们):

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

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

您尚未指定要使用的技术,因此我将举一个使用伪代码编写SQL DB的示例。对于MongoDB,您可以检查数据库如何为您进行这些检查。

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);
}

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

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

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

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