单个表上的死锁,在更新或插入语句上没有事务(?)

时间:2015-04-16 13:43:09

标签: c# sybase-ase database-deadlocks

所以,我们遇到了这些死锁问题,并尝试在一个简单的代码片段中复制它。基本上,我们在进行一些处理之前登录我们的数据库,然后用结果更新。

数据库是Sybase ASE 15.7,使用的驱动程序是Adaptive Server Enterprise ODBC驱动程序(v15.05)。代码是C#4.0

我用来复制问题的代码如下:

数据库:

create  table dbo.test_deadlock_t(
id   int  identity,
col1   int  not null,
col2   varchar(255)  not null,
constraint test_deadl_id_pk primary key clustered ( id ))
alter table test_deadlock_t lock allpages

C#(忽略一些声明性代码)

using System.Data.Odbc;

public const string cmdInsert = "INSERT INTO test_deadlock_t (col1, col2) VALUES (1, 'test') SELECT @@identity";
public const string cmdUpdate = "UPDATE test_deadlock_t SET col1 = 3, col2 = 'aaaaaaaa' WHERE id = {0}";

public static void Test()
{
    Task[] tasks = new Task[threadCount];
    for (int ii = 0; ii < threadCount; ii++)
    {
        tasks[ii] = Task.Factory.StartNew(() => InsertThenUpdate());
    }
    Task.WaitAll(tasks);
}

public static void Test2()
{
    Task[] tasks = new Task[threadCount];
    for (int ii = 0; ii < threadCount; ii++)
    {
        int ii_copy = ii;
        tasks[ii_copy] = Task.Factory.StartNew(() => Update(ii_copy));
    }
    Task.WaitAll(tasks);
}

public static void InsertThenUpdate()
{
    using (OdbcConnection connection = new OdbcConnection(connectionString))
    {
        connection.Open();
        OdbcCommand command = new OdbcCommand(cmdInsert, connection);
        int result = (int)(command.ExecuteScalar() as decimal?).Value;
        Update(result);
    }
}

public static void Update(int id)
{
    using (OdbcConnection connection = new OdbcConnection(connectionString))
    {
        connection.Open();
        OdbcCommand command = new OdbcCommand(String.Format(cmdUpdate, id), connection);
        int result = command.ExecuteNonQuery();
        Console.WriteLine("Update result : " + result);
    }
}

Test和Test2都会抛出随机死锁异常。我没有明确地发起交易,表和查询都很简单,任何人都可以解释发生了什么以及如何避免这个问题?

谢谢!

编辑:这个测试用例实际上并没有真正重现问题,我正在编辑这个,因为我觉得当更新设置varchar列的大小时会发生死锁。

正如杰森所提到的,僵局可能来自PK指数的更新。可能的原因可能是查询正在锁定表,意识到页面不够,将行移动到新页面,然后尝试锁定索引以在页面上持有锁定时更新,而另一个查询首先查询索引,然后要求锁定页面。

1 个答案:

答案 0 :(得分:2)

我不确定这会对未来的任何人有所帮助,但是未解决的问题很糟糕,所以我发现了这些:

仅当更新使行更长时才会出现此问题。杰森是正确的,建议锁可能来自索引。在对日志进行进一步分析之后,我们得到了这个:

Deadlock Id 29756: Process (Familyid 0, Spid 282) was waiting for a 'exclusive page' lock on page 26700113 of table 'test_deadlock_t' in database 'xxx' but process (Familyid 0, Spid 1051) already held a 'exclusive page' lock on it.
Deadlock Id 29756: Process (Familyid 0, Spid 1051) was waiting for a 'exclusive page' lock on page 29892374 of table 'test_deadlock_t' , indid 1 in database 'xxx' but process (Familyid 0, Spid 282) already held a 'exclusive page' lock on it.

“indid 1”指的是主键。

我的理解是sybase将页面锁定在要进行更新的位置。在此期间,选择以某种方式请求锁定索引。然后更新意识到页面对于更新的行来说太小,因此它尝试移动页面,并请求锁定索引。但是select已经有了一个锁,而select想要访问同一个页面......

如果有人对发生的事情有了更好的了解,我会很高兴知道。在我们的例子中,使用固定长度的字段解决了这个问题。