2015-04-16 149 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鎖定它希望做一個更新的頁面。同時,某個選擇以某種方式請求索引上的鎖定。然後,更新意識到頁面對於更新的行太小,所以它會嘗試移動頁面並請求索引的鎖定。但是選擇已經有一個鎖,並選擇要訪問同一頁面...

如果任何人有一個更好的瞭解所發生的事情,我會很高興知道的。在我們的例子中,使用固定長度的字段解決了這個問題。