处理并发全局“库存”更新的通用策略
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";
向数据库添加新名称时,您需要一个可以解决并发问题的唯一常量。
【讨论】:
以上是关于处理并发全局“库存”更新的通用策略的主要内容,如果未能解决你的问题,请参考以下文章