【问题标题】:Best way to deal with deadlock in SqlServer?处理 Sql Server 死锁的最佳方法?
【发布时间】:2015-06-24 15:25:05
【问题描述】:

我在 SqlServer 2008 上经常遇到死锁。目前,我的数据访问层中有以下代码来处理它。它基本上捕获了死锁并尝试使用新连接重新提交命令。没有交易。但这似乎不起作用 - 用户仍然会遇到异常,我的日志显示大多数异常都是死锁。谁能告诉我我做错了什么?谢谢。

private static SqlDataReader ExecDataReader(SqlCommand comm, CommandBehavior behavior)
    {
        try { return comm.ExecuteReader(behavior); }
        catch(SqlException ex)
        {
            if(ex.Number == 1205 && comm != null)
            {
                // Deadlock. Can't resubmit with the same connection,
                // have to recreate it.
                SqlParameterCollection pars = comm.Parameters;
                string str = comm.Connection.ConnectionString;
                string sproc = comm.CommandText;
                int t = comm.CommandTimeout;
                try
                {
                    comm.Cancel();
                    if(comm.Connection != null &&
                            comm.Connection.State != ConnectionState.Closed)
                        comm.Connection.Close();
                }
                catch { }
                // Trying to execute it after a random number of seconds
                // in order not to get a deadlock again by executing both
                // deadlocked commands at the same time. The GetRamdom
                // method works as expected, returns totally random number
                // in expected range
                Thread.Sleep(GetRandom());
                SqlConnection conn2 = new SqlConnection(str);
                conn2.Open();
                SqlCommand comm2 = conn2.CreateCommand();
                comm2.CommandText = sproc;
                comm2.CommandType = CommandType.StoredProcedure;
                comm2.CommandTimeout = t;
                CopyParameters(pars, comm2);
                return ExecDataReader(comm2, behavior);
            }
            else throw;
        }
    }

【问题讨论】:

  • 您必须查看导致死锁的原因才能解决您的问题。通常是两个连接尝试做同样的事情,但顺序不同。例如,如果进程 A 和 B 都放置了排他锁,连接 1 执行 A 然后 B,但连接 2 执行 B 然后 A,您可能会遇到每个连接都在等待另一个连接释放其锁的情况。根据我的经验,一个常见的原因是批量删除,因为它们很容易导致锁升级。尽量减少交易量也会有所帮助。
  • 偶尔的死锁是不可避免的,没关系。频繁和/或常规的死锁不是。它们通常是由您的 SQL 查询、数据设计和数据库配置的某种组合引起的。所以你需要向我们展示这些。您发布到带有 SQL 标记的问题的 C# 代码看起来不错。
  • 另一种避免死锁的方法是,如果您知道哪些代码位经常相互死锁,则使用sp_getapplock 在这些代码段周围放置一个“进程”锁。这会强制执行“关键路径”,以便一次只有一个进程可以尝试锁定名义资源。

标签: sql-server tsql sql-server-2008-r2 deadlock database-deadlocks


【解决方案1】:

读取导致死锁并不常见,但它可能会发生
只有当它与更新事务冲突时才会发生

查看所有阅读和更新

我知道我会为此受到抨击,但请尝试使用(无锁)读取器命令
你可以得到脏读,但你的读不会导致也不会成为死锁的受害者
如果可行,您可以尝试 rowlock

也许发布锁和更新命令

您想要的是一致的更新顺序
尝试分解更新
如果您确实需要在某些时候更新很多行,则最好使用 tabblock

另一件需要关注的事情是你已经进入和退出的读者
不要在 Reader 中进行处理。读取并保持连接打开
完成后立即关闭阅读器和连接

【讨论】:

  • 我完全忘记了不锁定阅读器的可能性。谢谢你提醒我!
【解决方案2】:

您的 C# 看起来不错。但有时由于各种原因,死锁是不可避免的。您需要检测仍然发生死锁的语句/存储过程,并使用 sp_getapplock 在服务器级别锁定该代码。

【讨论】:

  • 因为这是一个低质量的答案。您可以通过在代码中显示示例来改进。
  • 谢谢。但是 sp_getapplock 的使用可以有十几种编码方式。问题是关于解决死锁的方法,而不是关于如何编码任何特定方式的细节。通过查看他的代码示例,我可以看出作者似乎是一位经验丰富的程序员,他不是在寻找代码示例。
猜你喜欢
  • 2019-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多