SqlBulkCopy在事务内部插入时会阻止对表的任何其他写入

本文关键字:任何 其他 事务 内部 插入 SqlBulkCopy | 更新日期: 2023-09-27 18:26:23

在我的web应用程序中,用户可以同时插入大量数据,以提高我使用SqlBulkCopy类的性能。对于插入到两个不同表中的单个操作,它会运行多次。如果用户取消操作或操作失败,那么我需要回滚数据,以便使用隔离级别的快照将所有内容封装在事务中。

我的理解是,使用快照隔离将允许其他用户同时写入/读取表。但是,当进行一次数据上载时,它将阻止对表的任何其他写入,直到整个父事务完成。

以下是一些显示问题的简化代码。我排除了很多功能,但想法保持不变。我对内存中的某个集合进行迭代,将它们批量复制到一个表,取回它们,然后批量复制到另一个表。

using (var transaction = myDbContext.Database.BeginTransaction(
System.Data.IsolationLevel.Snapshot))
 {
   var myCollectionOfObjects;
    
   while(!GetData(ref myCollectionOfObjects))  
   {     
    SqlBulkCopy bulkCopy = new SqlBulkCopy(myCon, transaction);
    
    //Sets the columns + rows
    SetUp(bulkCopy);
    
    bulkCopy.WriteToServer();
    //After the bulkcopy operation is complete
    // we retrieve the rows inserted and do another bulk copy to a different table
    var recentlyAddedRows = GetRecentlyAddedRow();
    
    SqlBulkCopy otherTableBulkCopy = new SqlBulkCopy(myCon, transaction);
     
    SetUpBulkCopyForOtherTable(otherTableBulkCopy);
    otherTableBulkCopy.WriteToServer();         
  }
  transaction.Commit();
 }

因此,如果一个用户当前在该事务中,即使它正在回滚,也会阻止所有其他向表写入的事务,因此其他执行相同功能或试图向表写入内容的用户也会被阻止。

这是预期的行为吗?有没有办法绕过这一点?

编辑

通过查看SQL中应用的锁,这似乎是由于bulkcopy类导致在表对象上设置了一个排他锁(X),而就好像一次插入一个锁一样,表上只应用了一个意向锁(IX)。仍然不确定是否有办法解决这个问题,但我认为这是由于锁定升级。

在我的一些测试中,更改表索引上的"允许页面锁定"和更改大容量副本的批量大小已经绕过了完全锁定,但它们是不稳定的。

SqlBulkCopy在事务内部插入时会阻止对表的任何其他写入

IsolationLevel仅指读取,不指写入。如果您的一个客户端正在写入数据,那么另一个客户端应该能够读取事务开始前的数据,但是,它将无法同时写入。

要减少阻塞时间,可以做的一件事是大容量复制到(唯一的)暂存表中,而不是直接复制到目标表中。这根本不需要在事务中。一旦所有数据都在暂存表中,就将其复制到事务中的目标表中。这不会完全阻止阻止的可能性。然而,从数据库中复制数据通常会很快,尤其是当数据可能被缓存时。唯一潜在的棘手之处是创建uique暂存表(如果它们不是唯一的,只需将问题从一个表转移到另一个表)。

相关文章: