【问题标题】:Do batched SQL statements increase chances of deadlock errors?批处理 SQL 语句会增加死锁错误的几率吗?
【发布时间】:2018-09-30 15:04:53
【问题描述】:

我有一个连接到 TSQL 数据库的 C# 项目。该项目在单个表上运行多个顺序更新语句,例如:

 private void updateRows() {
      string update1 = "UPDATE WITH (ROWLOCK) table SET ... WHERE ...;"
      string update2 = "UPDATE WITH (ROWLOCK) table SET ... WHERE ...;"
      string update3 = "UPDATE WITH (ROWLOCK) table SET ... WHERE ...;"

      // execute updates one after the other
 }

理想情况下,这些语句将被批处理以避免多次往返于数据库:

string update = "
    UPDATE WITH (ROWLOCK) table SET ... WHERE ...;
    GO
    UPDATE WITH (ROWLOCK) table SET ... WHERE ...;
    GO
    UPDATE WITH (ROWLOCK) table SET ... WHERE ...;
    GO
";

我的问题是,如果语句是批处理的,这是否会增加由于表扫描而发生死锁错误的机会?

由于每条语句之间的时间较短,我想这可能会增加死锁的机会,因为一次更新可能会引入行或页锁,在执行下一条语句时可能不会释放。但是,如果更新语句没有被批处理,那么在每个更新语句之间有更多的时间来释放行锁或页锁,因此发生死锁的可能性较小。

【问题讨论】:

  • 为什么要使用 ROWLOCK 提示?封闭交易在哪里?我可以建议您解释一下您要解决的实际问题吗?您可能有 XY 问题...
  • @MitchWheat 过去,顺序更新会导致页面锁定,因此在执行后续更新时会出现死锁。
  • “GO”不是 SQL 语言的一部分,在 C# 命令中不需要将顺序语句放在一起。
  • 没有一个答案是正确的!为什么还需要单独的“更新产品维度”?
  • @MitchWheat 没什么,事实上他们已经在进行中了。我只是想我可以就 SO 提出一个可能与正在进行的工作无关的问题,以防它可能受到影响,但是如果没有受到指责,情况似乎并非如此。

标签: c# database tsql batch-processing deadlock


【解决方案1】:

我猜你不会喜欢我的回答,这是我的 2 美分,让我试着解释一下

  1. 首先,您的行锁可能不起作用,您最终可能会获得 DDL 的表锁,不允许 SQL Server 在您的事务中应用行锁定。
  2. SQL 喜欢集合操作,它在您一次更新大型数据集时表现出色。

我有类似的问题,我需要更新大量用户事务,但系统中没有备用 IO。我最终使用了“类似 ETL”的更新,

在 C# 中,我使用批量插入一次性获取数据库中的所有数据。这是我的方法。

protected void BulkImport(DataTable table, string tableName)
{
    if (!CanConnect)
        return;

    var options = SqlBulkCopyOptions.FireTriggers | SqlBulkCopyOptions.CheckConstraints |
                    SqlBulkCopyOptions.UseInternalTransaction;
    using (var bulkCopy = new SqlBulkCopy(_con.ConnectionString, options))
    {
        bulkCopy.DestinationTableName = tableName;
        bulkCopy.BulkCopyTimeout = 30;
        try
        {
            lock(table){
            bulkCopy.WriteToServer(table);
            table.Rows.Clear();
            table.AcceptChanges();
            }
        }
        catch (Exception ex)
        {
            var msg = $"Error: Failed the writing to {tableName}, the error:{ex.Message}";
            Logger?.Enqueue(msg);
            try
            {
                var TE= Activator.CreateInstance(ex.GetType(), new object[] { $"{msg}, {ex.Message}", ex });
                Logger?.Enqueue(TE as Exception);
            }
            catch
            {
                Logger?.Enqueue(ex);

            }

        }
        finally
        {
            bulkCopy.Close();
        }
    }
}

请注意,DataTable 不是线程安全的,您需要在与 DataTable 交互(插入行、清除表)时锁定它。

然后,我将数据转储到临时表中,并使用合并语句将数据带入我需要的数据库中。

我在 50 个左右的表上每秒执行 +100k 条记录,到目前为止还没有任何性能或死锁问题。

【讨论】:

    猜你喜欢
    • 2010-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多