【问题标题】:c# lock table for bulkcopyc# lock table for bulkcopy
【发布时间】:2014-01-22 06:47:23
【问题描述】:

我在 c# 中有一个数据表,它每隔几毫秒插入一个新行。我希望这个数据表每 10 秒在 SQL Server 中进行一次大容量复制,但我得到一个异常“集合已修改;枚举操作可能不会执行”所以我使用锁来绕过该错误。锁定是一种好方法还是有更好的选择?

lock (testdatatable.Rows.SyncRoot)
{
     s.WriteToServer(testdatatable);
}

【问题讨论】:

    标签: c# sql sql-server locking


    【解决方案1】:

    这个问题与以下无关:

    • SQL 服务器
    • 锁定(嗯,有点,见答案的底部)

    这里的根本问题是批量复制将枚举集合。在发生这种情况的同时,您正在更改集合。

    就是这样。

    .NET 中几乎所有的枚举器实现都将与它枚举的集合协作,如果集合在枚举器仍然存在时发生更改,则枚举器将停止工作。

    例子:

    • 我说,对于此页面上的每个人,我想拨打列出的电话号码并与此人交谈
    • 在我执行此操作时,您向页面添加了一个新人

    问题:

    • 我是否也应该给那个人打电话,因为他现在在页面上,当我拨打最后一个(就像我开始时一样)并完成那个呼叫时,下一个人应该是你添加的那个,所以我应该给那个人打电话?
    • 不要理会那个人,因为我开始时他/她不在页面上?

    如果您在我正在拨号或与该人交谈时将其从列表中删除怎么办?我应该挂断电话吗?

    如果在您对它进行枚举时基础集合发生变化,大多数 .NET 集合枚举器将停止工作,确切的例外是。

    “修复”此问题的常用方法是制作集合的副本。

    基本上,而不是这样做:

    foreach (var x in y)
    

    你这样做:

    foreach (var x in y.ToArray())
    

    .ToArray() 调用会将副本复制到一个单独的数组中,然后整个 for 循环将对其进行枚举。如果y 同时发生变化,那没关系。

    但是,在您的情况下,您离正在执行的实际 for 循环或类似的循环还很远。

    我会确保这里有问题的 DataTable 在您将其批量复制到数据库中时不会更改。

    锁不是正确的做法,至少本身不是,因为任何其他不锁的代码当然会完全忽略锁。 lock 语句不会神奇地阻止其他线程更改实例,它只是在实例上放置一个锁其他尝试锁定的线程将停止这样做。如果在修改该对象之前没有其他线程尝试对该对象进行锁定,则您的锁定基本上不会做任何事情。

    【讨论】:

    • 非常感谢您的回答!我明白这正是我面临的问题。此外,如果我尝试复制一张表,我会收到完全相同的错误消息,因为同时插入了一个新行。目前,我使用 2 个相同的表,在批量复制命令之前,我将活动表更改为第二个,以便应用程序的其余部分将行插入到另一个表中。在下一次批量复制中,我再次将活动表更改为第一个并循环执行此过程。我只是想在我的问题中找到一个更优雅的解决方案
    【解决方案2】:

    也许这个问题应该用“c#”而不是 sql 来标记?

    您的问题(我认为)与您修改集合有关,对吗? 这是一个普遍的问题,我认为您可以在这里找到解决方案:

    "Collection was modified..." Issue

    如果没有,那么我们将不得不查看您的代码以进行更多调查。

    【讨论】:

      【解决方案3】:

      我不是 C# 专家,但我喜欢玩任何与 SQL 服务器相关的东西。

      对我来说,您将 ADO 数据表锁定在内存中。由于您没有发布其余代码,因此我无法判断这是否与 SQL Server 表相关联。我敢打赌,它是。

      消息表明底层数据已被修改。

      一个批量复制过程是否会踩到另一个?

      查看 ConcernedOfTunbridgeWells 的条目以获得良好的编码风格 -

      Best Practices for uploading files to database

      我的主要问题是你为什么每 10 秒做一次?

      在这种频率下,会想到一些事情。

      1 - 您使用的是没有索引和完整性的临时表。如果数据量很大,重建索引需要时间。

      2 - 底层 SqlBulkCopy 类对锁做了什么。 TABLOCK 可能会导致阻塞。

      如果您有数据库的系统管理员访问权限,这里有一些简单的命令可以查看锁。

      --
      -- Locked object details
      -- 
      
      -- Old school technique
      EXEC sp_lock
      GO
      
      -- Lock details
      SELECT
          resource_type, resource_associated_entity_id,
          request_status, request_mode,request_session_id,
          resource_description 
          FROM sys.dm_tran_locks
          WHERE resource_database_id = DB_ID('AdventureWorks2012')
      GO
      
      -- Page/Key details
      SELECT object_name(object_id) as object_nm, *
          FROM sys.partitions
          WHERE hobt_id = 72057594047037440
      GO
      
      -- Object details
      SELECT object_name(1266103551)
      GO
      

      批量复制上的 Tech Next sn-p。

      http://technet.microsoft.com/en-us/library/ms130809.aspx

      TABLOCK:在大容量复制操作期间获取表级锁。此选项显着提高了性能,因为仅在大容量复制操作期间持有锁可减少表上的锁争用。如果表没有索引并且指定了 TABLOCK,则可以由多个客户端同时加载表。默认情况下,锁定行为由表选项 table lock on bulk load 确定。

      总而言之,尝试增加 BULK COPY 操作之间的时间。看看错误是否消失。

      【讨论】:

        猜你喜欢
        • 2017-02-18
        • 1970-01-01
        • 2014-06-25
        • 2019-08-29
        • 2019-12-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多