2015-04-16 108 views
1

所以,我们一直有这些死锁问题,我想在一个简单的代码片段复制此。基本上,我们在进行一些处理之前先登录我们的数据库,然后更新结果。死锁在单台无交易(?)上更新或插入语句

数据库是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); 
    } 
} 

两个测试和测试2抛出随机死锁例外。我并不明确地启动事务,表和查询都很简单,任何人都可以解释发生了什么以及如何避免这个问题?

谢谢!

编辑:本次测试的情况下,其实并不是真的重现问题,我编辑这个,因为我有,当更新设置一个更大尺寸的VARCHAR列发生死锁的感觉。

贾森mentionned,僵局可能会从PK索引的更新到来。一个可能的原因可能是查询锁定表,意识到页面不够用,将行移动到新页面,然后尝试锁定索引以更新,同时在页面上持有锁定时,另一个查询从查询索引开始,然后要求在页面上锁定。

+0

为了测试完整性,如果您进行明确的事务处理,会发生什么情况? – Ralf

+1

我的猜测是它与此相关:http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.dc20021_1251/html/locking/locking7.htm。查看最后一段'在很多情况下,由所有页锁定导致的并发问题来自索引页锁,而不是数据页本身的锁。 –

+0

@Ralf:我在之前的测试中失败了(对于垃圾邮件抱歉),但行为与显式事务声明基本相同。 – vpi

回答

1

我不知道这将帮助任何人在未来,但没有答案的问题吸,所以这里是我发现:当更新使得该行不再只出现

的问题。贾森是正确的暗示锁可能来自索引。经过对日志的进一步分析,我们得到这个:

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锁定它希望做一个更新的页面。同时,某个选择以某种方式请求索引上的锁定。然后,更新意识到页面对于更新的行太小,所以它会尝试移动页面并请求索引的锁定。但是选择已经有一个锁,并选择要访问同一页面...

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