在我的网络应用中,用户可以一次插入大量数据,以提高我使用SqlBulkCopy类的性能。对于插入两个不同表的单个操作,它会运行多次。如果用户取消操作或失败,那么我需要回滚数据,因此我使用隔离级别快照将所有内容包装在事务中。
据我了解,使用Snapshot隔离将允许其他用户同时写入/读取表。但是,当一个数据上载发生时,它将阻止对表的任何其他写入,直到整个父事务完成。
以下是一些显示问题的简化代码。我排除了很多功能,但这个想法保持不变。我迭代了一些内存集合,将它们批量复制到一个表中,将它们退回,然后批量复制到另一个表。
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)。仍然不确定是否有办法解决这个问题,但我认为这是由于锁定升级造成的。
更改表索引上的允许页锁定以及更改批量副本的批量大小已经在我的一些测试中完全锁定,但它们是不稳定的。
答案 0 :(得分:2)
IsolationLevel
仅指读取而不是写入。如果您的某个客户端正在写入数据,则另一个客户端应该能够在事务开始之前读取数据,但是,它将无法同时写入。
答案 1 :(得分:1)
减少阻塞时间可以做的一件事是批量复制到(唯一的)临时表而不是直接复制到目标表。这根本不是在交易中。一旦您的所有数据都在临时表中,请将其复制到事务中的目标表中。这不会完全阻止阻塞的可能性。但是,从数据库中复制数据通常会很快,特别是因为数据很可能被缓存。唯一可能有点棘手的问题是创建uique staging表(如果它们不是唯一的,你只需将问题从一个表移到另一个表)。