举一个简化的例子:
我有一个包含一个表的数据库:名称,其中有100万条记录,每条记录包含一个普通的男孩或女孩的名字,每天都有更多的记录。
我有一个应用程序服务器,它使用我的网站“名称选择器”从父母那里获取http请求。对于每个请求,我需要从数据库中获取一个名称并将其返回,然后不将该名称提供给另一个父项。服务器是并发的,因此可以处理大量请求,但必须遵守“每个请求的唯一名称”,并且仍然具有高可用性。
此用例的体系结构的主要组件和策略是什么?
据我所知,您有两个操作:添加名称和选择名称。
我有几个问题:
假设您不想让所有名称选择请求彼此等待(通过锁定排队):
在仅选择名称的情况下解决并发性的一种解决方案是使用Optimistic offline lock。
最常见的实现是向表中添加版本字段,并在将名称标记为已选择时递增此版本。您将需要数据库支持,但大多数数据库都为此提供了一种机制。 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);
}
在将whis对象保存到DB之前,必须执行版本比较。 SQL提供了一种基于某些条件执行查询的方法,并返回受影响行的行数。在我们的例子中,我们检查数据库中的版本是否与更新前的旧版本相同。如果不是,则表示其他人已更新记录。
在这个简单的例子中,您甚至可以删除版本字段并在SQL查询中使用isChosen标志,如下所示:
var query = "UPDATE records SET .....
WHERE id = {record.id} AND isChosend = false";
向数据库添加新名称时,您将需要一个可解决并发问题的Unique约束。