锁定/同步问题-后端代码的执行速度比数据库保存快

问题描述

下面我有一个有关为新发票生成发票编号的简单示例:

// 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'