【问题标题】:ASP.NET Web API C# Concurrent Requests Causing Duplicates in DatabaseASP.NET Web API C# 并发请求导致数据库重复
【发布时间】:2018-05-30 17:45:50
【问题描述】:

我有一个 WebApi Async 控制器方法,它调用另一个异步方法,该方法首先执行 null 检查以查看记录是否存在,以及是否不将其添加到数据库中。问题是,如果我说 3 个请求同时进入,所有空检查同时在各个线程中发生(我假设),我将得到 2 个重复条目。例如:

 public async void DoSomething()
{
    var record = {query that returns record or null}
    if (record == null)
    {
        AddNewRecordToDatabase();
    }   
}

...这似乎是一件很常见的事情,也许我遗漏了一些东西,但是我该如何防止这种情况发生呢?当然,我必须有意识地尝试让它创建副本,但必须不允许它这样做。

提前致谢,

【问题讨论】:

  • 看看这个链接。它描述了如何使用 EntityFramework 实现乐观锁定机制。另一种选择是使用互斥锁。 docs.microsoft.com/en-us/aspnet/mvc/overview/getting-started/…
  • @Matt 乐观锁定是一种很好的更新方法,但我不确定它如何用于插入,因为没有记录可以检查更改。
  • OP:两个问题。 (1) 期望的行为是什么?如果两个用户一次添加相同的东西,您是否希望添加两条记录,但您只需要确保标识符是唯一的?还是您希望第二个用户看到错误? (2) 您是在使用任何类型的实体框架,还是使用直接 SQL 调用?
  • @John Wu - 同一个端点同时被同一个请求击中的不是用户。如果记录不存在,我想添加它。如果它确实存在,我只想忽略它。谢谢:)

标签: c# asynchronous concurrency asp.net-web-api2


【解决方案1】:

我通过调用存储过程来防止异步调用发生这种情况。 然后,存储过程通过“检测重复键”或对 MSSQL 数据库的类似查询进行检查。

这样,只有异步调用的顺序才能确定哪个是创建,哪个不是。

【讨论】:

  • 如果您的开发团队擅长编写存储过程,这是最好的方法;由于锁定的位置,它将在高容量站点上表现最佳并具有最大的吞吐量。
  • 谢谢,不过我会调查一下,执行更新的方法也会执行一堆其他相关的插入操作,所以我不得不考虑将所有这些逻辑也移到存储过程中。跨度>
【解决方案2】:

我会通过在数据层中放置独特的约束来解决这个问题。假设您的数据源是 sql,您可以使用“返回记录或 null 的查询”在要查询的列之间设置唯一约束,它将防止这些重复。使用锁或互斥锁的问题在于它不能跨服务的多个实例进行扩展。您应该能够部署您的服务的许多实例(到不同的机器),让这些实例中的任何一个处理请求,并且仍然具有一致的行为。在这种情况下,互斥锁或锁不会保护您免受这种并发问题的影响。

【讨论】:

    【解决方案3】:

    对此有几个答案,具体取决于细节和您的团队的满意程度。

    • 最好和最高效的答案是修改您的 c# 代码,以便调用您编写的存储过程,而不是调用 CRUD 数据库操作。存储过程将检查是否存在并仅在需要时插入或更新。由于您编写代码,因此细节完全在您的控制之下。

    • 如果您想坚持使用普通的 CRUD 操作,您可以通过将请求包装在事务中并使用严格的事务隔离级别来强制数据库将请求一个接一个地序列化。在 SQL Server 上,您希望使用可序列化。这将防止任何事务在您检查存在的部分和插入记录的部分之间的短时间内改变表的状态。有关事务隔离级别的列表以及如何在 c# 代码中应用它们,请参阅 this article。如果你这样做,就会有死锁的风险,所以你需要catch 并吞下这些特定的错误。

    • 如果你只需要它来保证唯一性,并且新记录有natural (not surrogate) key,你可以在key上添加唯一性约束,这样会阻止第二次插入成功。该解决方案不适用于代理键;它并没有真正解决问题,它只是将其重新定位到代理密钥生成过程。但是如果你有一个不错的自然密钥,这很容易实现。

    【讨论】:

    • 在 SQL Server 中使用 SERIALIZABLE 将防止重复,但会产生死锁而不是重复。要正确序列化事务,您必须使用 UPDLOCK(可能除了 SERIALIZABLE 来强制范围锁定)。
    • "在作为 INSERT 语句目标的表上指定 UPDLOCK 提示的功能将在 SQL Server 的未来版本中删除。这些提示不会影响 INSERT 语句的性能。避免在新的开发工作,并计划修改当前使用它们的应用程序。” link。确实,您会遇到一些僵局——并非总是如此,只有在时机恰到好处的情况下——并且 OP 需要抓住并吞下这些僵局。不要认为这是可以避免的。
    • UPDLOCK 位于搜索重复项的 SELECT 上,而不是 INSERT 上。如果您愿意捕获错误,那么只需在列上设置唯一约束,然后捕获键违规。
    • 您无法锁定不存在的记录。第三个项目符号介绍了唯一性约束及其限制。
    • 您当然可以锁定一条不存在的记录。这正是 SERIALIZABLE(或 HOLDLOCK)所做的。只是没有UPDLOCK,范围锁是S锁,两个会话都可以获取同一个。
    猜你喜欢
    • 1970-01-01
    • 2015-11-28
    • 1970-01-01
    • 2018-07-10
    • 2019-02-08
    • 1970-01-01
    • 2019-10-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多