问题描述
// API generate invoice number
public void PublishInvoice(Object invoiceData)
{
lock (companyInfo) // (1) if request is the same company will be blocked
{
int invoiceNo = GetNextNo(); // (2) get new invoice number from database (next no = current no + 1)
invoiceData.InvoiceNo = invoiceNo; // (3) set invoice number to invoice data
Save(invoiceData); // (4) save invoice data to database
}
}
我遇到的问题是,如果两个请求同时调用此API,则第二个请求将在(1)被阻止,当第一个请求完成执行(在4之后),然后第二个请求执行(2)以获取下一个发票编号,但是第一个请求的发票尚未保存到数据库中,因此第二个发票的发票编号与第一个发票的编号相同。
我的问题是我该如何处理这个问题。当我阻塞数据库时,我对性能感到焦虑(我想到触发器,约束或事务)
其他:我创建了一个触发器,它将在创建新发票之前触发,它将检查发票编号是否可用,并且可以正常工作,但是我希望它将有一个更好的解决方案(基于可用性技术),b / c我不需要检查每个插入请求。
解决方法
如@Fildor所述:
大多数SQL DB提供了一种具有自动递增列的方法。
不仅限于SQL。因此,如果使用它,可能根本不需要lock (companyInfo)
。此外,如果您的代码是某些Web api的一部分,则可能甚至没有相同的companyInfo
实例。
使用数据库的 atomic 增量功能会更好。 例如,对于Mongo DB:
var collection = Get your collection here
var filter = Builders<BsonDocument>.Filter.Eq("AtomicNumbers","Invoices");
var update = Builders<BsonDocument>.Update.Inc("Counter",1);
var options = new FindOneAndUpdateOptions<BsonDocument,BsonDocument>() { IsUpsert = true,ReturnDocument = ReturnDocument.After };
var result = collection.FindOneAndUpdate(filter,update,options);
int newInvoiceNumber = result["Counter"].AsInt32;
MSSQL示例:
+------------+---------+
| id | Counter |
+------------+---------+
| Invoices | 2 |
+------------+---------+
| OtherStuff | 17 |
+------------+---------+
UPDATE AtomicNumbers SET Counter = Counter + 1 where id = 'Invoices'