防止在事务中锁定数据库

时间:2015-07-22 05:58:14

标签: sql sql-server entity-framework

我有查询更新和插入大量数据

update [TableA]
set ColumnA1 = 'ValueA1'

DECLARE @MyCursor CURSOR;
DECLARE @MyField char(16);
BEGIN
    SET @MyCursor = CURSOR FOR
    select  ColumnA1 from [TableA]
        Where ColumnA2 = ValueA2  

    OPEN @MyCursor 
    FETCH NEXT FROM @MyCursor 
    INTO @MyField

    WHILE @@FETCH_STATUS = 0
    BEGIN

      FETCH NEXT FROM @MyCursor 
      INTO @MyField 

      Insert Into TableB(ColumnB1, ColumnB2, ColumnB3, ColumnB4, ColumnB5, ColumnB6)
      Values(@MyField, ValueB1, ValueB2, ValueB3, ValueB4, ValueB5)
    END; 

    CLOSE @MyCursor ;
    DEALLOCATE @MyCursor;
END;

首先我注意到我更新了TableA,然后使用更新的TableA记录将一些日志信息插入TableB。我想以事务方式执行整个sql命令。我担心使用begin transaction锁定整个sql server数据库。我已经使用了Entity Framework,但我还不知道如何在不锁定sql server的情况下处理这类命令。

1 个答案:

答案 0 :(得分:1)

根据Hamlets评论,没有“整个数据库”lock。最接近的可能是将数据库放入single user mode

但是,在高度争用的数据上对行或表进行广泛(独占)锁定会影响同时尝试读取已提交数据或其他编写器的其他查询的性能。

e.g。由于下面的更新可能会更改所有行,因此在TableA上几乎肯定会TABLOCK,因此如果您将整个发布的SQL包装在事务中,TableA也将在游标持续时间内被锁定。 / p>

UPDATE [TableA]
SET ColumnA1 = 'ValueA1';

似乎光标本身是不必要的并且是不可取的 - 只要有可能,用基于集合的方法替换RBAR方法。以下插入应该是等效的:

INSERT Into TableB(ColumnB1, ColumnB2, ColumnB3, ColumnB4, ColumnB5, ColumnB6)
SELECT ColumnA1, ValueB1, ValueB2, ValueB3, ValueB4, ValueB5
FROM [TableA]
WHERE ColumnA2 = ValueA2;

要回答您的最终问题,默认情况下,实体框架会将.SaveChanges() call包装到a transaction - 到目前为止,对DbContext所做的所有更改都只会在内存中进行跟踪。

IMO,像EntityFramework这样的ORMS不适合大型批量/批量操作,例如你在这里提供的sql,因为你几乎无法控制生成的Sql。

如果您要将此批量作业转移到.Net,我可以建议

  • 您的批量更新可以通过基本SqlCommand
  • 完成
  • 使用SqlBulkCopy
  • 可以有效地进行批量插入

您将能够使用共享SqlConnection控制上述事务(SqlBulkCopy有一个构造函数采用SqlConnection)。

顺便说一句,如果您的要求不要求您在事务下执行整个操作序列,则可以考虑将更新+插入批处理批量转换为<每次提交5000,以避免lock escalation issues