16

当我尝试从 C# 中的 SQL 数据库中获取数据时,产生了这两个异常:

System.Data.SqlClient.SqlException:事务(进程 ID 97)与另一个进程在锁资源上死锁,并已被选为死锁牺牲品。

或者

System.Data.SqlClient.SqlException:事务(进程 ID 62)在锁定资源上与另一个进程死锁,并已被选为死锁牺牲品。

或者

System.Data.SqlClient.SqlException:事务(进程 ID 54)与另一个进程在锁资源上死锁,并已被选为死锁受害者。重新运行事务。

这是代码:

 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];
 }

这些每次都发生。

关于如何解决这些问题的任何想法?

4

3 回答 3

26

您可以采取一些措施来减少收到的死锁数量,还可以采取一些措施来完全消除它们。

首先,启动 SQL Server Profiler 并告诉它给你一个死锁图。运行此跟踪将告诉您另一个与您的冲突的查询。您的查询非常简单,尽管我严重怀疑您是否SELECT *在系统中对名为 MyTable 的表进行了查询...

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

您可以做的其他事情:

  • 除其他外,通过对查询应用正确的索引来加快查询速度。
  • 在数据库上启用快照隔离并SET TRANSACTION ISOLATION LEVEL SNAPSHOT在适当的情况下在您的事务中使用。还可以使用 row-versioning 启用读取提交。在许多情况下,这足以完全消除大多数死锁。阅读有关事务隔离级别的信息。 了解你在做什么。
于 2010-11-30T14:08:57.320 回答
10

并不是说这会帮助解决死锁问题,但您应该像处理您的IDisposable对象一样处理其他对象SqlConnection

    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'

不过我想清楚一点,您允许使用此解决方案读取未提交的数据。这是交易系统中的风险。阅读这个答案。希望这可以帮助。

于 2010-11-30T14:36:21.403 回答
3

基本上,SQL Server 并发模型使您永远无法避免此异常(例如,如果它们碰巧锁定相同的索引页面或其他东西,完全不相关的事务可能会互相阻塞)。您能做的最好的事情就是让您的交易保持简短以减少可能性,如果您遇到异常,请按照它所说的去做并重试交易。

于 2010-11-30T13:58:09.897 回答