SqlException: 死锁

18

在尝试从SQL数据库中获取数据时,我遇到了以下两个异常:

System.Data.SqlClient.SqlException: Transaction (Process ID 97) was deadlocked on lock resources with another process and has been chosen as the deadlock victim.

或者

System.Data.SqlClient.SqlException: Transaction (Process ID 62) was deadlocked on lock resources with another process and has been chosen as the deadlock victim.

或者

System.Data.SqlClient.SqlException: Transaction (Process ID 54) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

这是我的代码:

 using (SqlConnection con = new SqlConnection(datasource))
 {
    SqlCommand cmd = new SqlCommand("Select * from MyTable Where ID='1' ", con);
    cmd.CommandTimeout = 300;
    con.Open();
    SqlDataAdapter adapter = new SqlDataAdapter(cmd);
    DataSet ds = new DataSet();
    adapter.Fill(ds);
    con.Close();
    return ds.Tables[0];
 }

每次都发生这些事情。

有什么想法可以解决这些问题吗?


2
你是第一次遇到这个异常还是每次尝试都会出现? - Alexis Dufrenoy
4
可能有其他查询同时运行,这就是为什么会出现死锁。两个或多个查询想要访问相同的数据。我们需要查看所有正在运行的查询,而不仅仅是成为死锁受害者的那个查询。 - Colin Mackay
7
像这样的查询无法导致死锁,它只需要在1个表中对1行进行读取锁。即使另一个进程拥有该锁,它也会愉快地等待直到请求超时。要发生死锁,通常Process1具有锁A并正在等待锁B;而Process2具有锁B并正在等待锁A。必须涉及其他查询。运行SQL Server Profiler(http://msdn.microsoft.com/en-us/library/ms187929.aspx)并监视正在执行的数据库查询。 - sheikhjabootie
2
更多性能分析器和死锁分析链接:(1) http://support.microsoft.com/default.aspx?scid=kb;en-us;832524 (2) http://msdn.microsoft.com/en-us/library/ms188246.aspx (3) http://msdn.microsoft.com/en-us/library/ms190465.aspx - Marek Grzenkowicz
1
Jeff Atwood之前遇到了同样的异常:Coding Horror: Deadlocked!。他的文章可能会有所帮助 :-) - Christophe Keller
显示剩余2条评论
3个回答

26

有几件事情可以减少死锁数量,有些事情可以完全消除死锁。

首先,启动SQL Server Profiler并告诉它给你一个死锁图。运行此跟踪将告诉您与您的查询冲突的其他查询。虽然您的查询非常简单,但我严重怀疑您在系统中有一个名为MyTable的表的SELECT *查询...

无论如何,拥有死锁图和其他查询,您应该能够确定哪些资源正在死锁。经典解决方案是更改两个查询的顺序,使资源按相同顺序访问--这避免了循环。

其他您可以做的事情:

  • 通过应用正确的索引等方式加快查询速度。
  • 在数据库上启用快照隔离,并在适当的事务中使用SET TRANSACTION ISOLATION LEVEL SNAPSHOT。还启用读取提交带有行版本控制。在许多情况下,这足以完全消除大多数死锁。阅读有关事务隔离级别的文章。理解您正在做什么。

9

虽然这并不能解决死锁问题,但你应该像处理 SqlConnection 一样处理其他的 IDisposable 对象,如下:

    using (SqlConnection con = new SqlConnection(datasource))
    using (SqlCommand cmd = new SqlCommand("Select * from MyTable Where ID='1' ", con))
    {
        cmd.CommandTimeout = 300;
        con.Open();
        using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
        using (DataSet ds = new DataSet())
        {
            adapter.Fill(ds);
            return ds.Tables[0];
        }
    }

您可以通过在查询中使用锁提示来避免锁定,例如:

Select * from MyTable with (nolock) Where ID='1'

我想澄清一下,使用这个解决方案允许读取未提交的数据。在事务性系统中这是一种风险。阅读这个回答。希望这可以帮到你。


3
基本上,SQL服务器并发模型使得你永远无法避免这个异常(例如,如果完全不相关的事务恰好锁定相同的索引页或其他东西,则可能会相互阻塞)。你能做的最好的办法是保持事务短小,以减少可能性,如果出现异常,请按照指示重试该事务。

你混淆了阻塞和死锁的概念。这个异常并非不可避免。 - Dave Markle
不,我不是。如果您使用默认锁定(有时是行锁,有时是页锁),很容易看到两个不相关的事务可能会意外地死锁对方(至少如果它们在事务内更新了两个表,并且您没有使用提示来确保表总是以相同的顺序读取)。对于行锁情况,这一点不太明显,但索引可能会对您造成影响。 - erikkallen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接