【问题标题】:Locking/Synchronize problem - Backend code execute faster than database saving锁定/同步问题 - 后端代码执行速度比数据库保存快
【发布时间】:2020-08-25 08:29:02
【问题描述】:

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

// 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 我不想检查每个插入请求。

【问题讨论】:

  • 那是什么数据库系统?大多数 SQL 数据库都提供了一种自动增加列的方法。
  • 还有:GetNextNo 是如何实现的?如果您有某种“序列”,这应该不是问题,它将具有原子“增量和获取”功能。
  • 我使用 SQL Server 数据库。我不能使用自动增量,因为发票编号取决于其他一些字段。有了sequence,当你调用sequence,生成下一个数字,但是这个事务失败了,sequence不会回滚,我的业务会出错。
  • 我明白了。我认为在这种情况下,您会遇到包括创建发票编号和插入在内的事务。
  • 是的,如果出现问题,将不会创建发票和发票编号

标签: c# database transactions synchronization locking


【解决方案1】:

正如@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'

【讨论】:

  • AtomicNumbers是手动创建的表吗? newInvoiceNumber 结果是否与我的函数 GetNextNo() 的结果相同(都从数据库中获取)。当 AtomicNumbers 还没有完全保存(并且第二个请求即将到来)时,我会得到同样的错误。
  • 是的,所有这些表都是手动创建的。您可以在您的GetNextNo() 中使用它们,并为invoiceNo 返回int 值。您不会得到相同的错误,因为 atomic 操作确保您每次得到不同的invoiceNo。
  • 您的意思是相同的自动增量机制。我以前没有听说过原子操作。你能给我一些这方面的文件吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-09-23
  • 1970-01-01
  • 2021-03-19
  • 2020-05-14
  • 2021-11-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多