2010-04-01 88 views
7

我在我的mysql事務中遇到死鎖錯誤。MySQL返回死鎖插入行和FK被鎖定'更新'

我的情況簡單的例子:

Thread1 > BEGIN; 
Query OK, 0 rows affected (0.00 sec) 

Thread1 > SELECT * FROM A WHERE ID=1000 FOR UPDATE; 
1 row in set (0.00 sec) 

Thread2 > BEGIN; 
Query OK, 0 rows affected (0.00 sec) 

Thread2 > INSERT INTO B (AID, NAME) VALUES (1000, 'Hello world'); 
[Hangs] 

Thread1 > INSERT INTO B (AID, NAME) VALUES (1000, 'Hello world2'); 
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction 

Thread2 > 
Query OK, 1 row affected (10.00 sec) 

B.AID是外鍵指A.ID

我看到三種解決方案:

  1. 捕獲死鎖錯誤代碼並重試查詢。
  2. 使用innodb_locks_unsafe_for_binlog在my.cnf
  3. 鎖(用於更新)表A中的線程2插入

之前有沒有其他解決辦法?

+0

我可以;看看爲什麼這將是一個僵局,你的標題提到FK的。 A和B之間是否有外鍵關係,你沒有提及 – Elemental 2010-04-01 14:16:12

+0

@Elemental:是的,有一個外鍵。 OP提到這一點,但有點神祕。我修改了帖子來說明問題。 – 2010-04-02 08:08:46

回答

1

我不知道這個例子的代碼是什麼,但是對於兩個線程來說可能都值得使用LOCK IN SHARE MODE,因爲你實際上並沒有更新這個行本身。如果你必須使用LOCK FOR UPDATE,我會認爲鎖定其他線程將是唯一的邏輯路徑。

另外,如果你打開離開MySQL,我發現PostgreSQL有更好的解決死鎖問題。在某些情況下,每當在> 1線程上運行相同的腳本時,我發現MySQL會死鎖。 PostgreSQL中相同的腳本可以處理任何數量的並行線程。

+0

'鎖定共享模式'在我的項目中不可接受; ( – user306814 2010-04-07 09:44:12

+0

+1。雖然它看起來不是最好的解決方案,但它比LOCK FOR UPDATE好得多。 – 2010-04-08 09:19:54

0

基於從mysql high performance blog.

一個功能,我能夠實現以下僵局PHP處理代碼:

/* maximum number of attempts for deadlock */ 
$MAX_ATTEMPS = 20; 

/* query */ 
$sql = "INSERT INTO B (AID, NAME) VALUES (1000, 'Hello world')"; 

/* current attempt counter */ 
$current = 0; 

/* try to query */ 
while ($current++ <$MAX_ATTEMPS) 
{ 
    $result = mysql_query($sql); 
    if(!$result && (mysql_errno== '1205' || mysql_errno == '1213' )) 
     continue; 
    else 
     break; 
} 
} 

希望這可能會給你一些好的想法。

+0

這是很好的解決方案,但我想要找到其他路徑 – user306814 2010-04-07 09:43:14

+0

當事務中只有INSERT語句時,這可能是一個很好的解決方案。但總的來說,這是不可接受的,因爲事務開始後你必須重複整個操作,這可能會跨越幾個函數(方法)。 – 2010-04-08 08:56:50

0

這裏沒有死鎖,你使用什麼版本的MySQL和什麼隔離級別? 我得到了這些結果,添加時間戳列於表B:

Thread1 > BEGIN; 

Thread1 > SELECT * FROM A WHERE ID=1000 FOR UPDATE; 
/* 0 rows affected, 1 rows found */ 

Thread2 > BEGIN; 

Thread2 > INSERT INTO B (AID, NAME, date) VALUES (1000, 'Hello world', NOW()); 
[Hangs] 

-- after 5 seconds 

Thread1 > INSERT INTO B (AID, NAME, date) VALUES (1000, 'Hello world2', NOW()); 
/* 1 rows affected, 0 rows found */ 

Thread1 > COMMIT; 

Thread2 > COMMIT; 

B就包含2行,看起來像:

  1. 1000 '世界,你好'「2011-06-11 19:23: 15'
  2. 1000'你好world2‘2011-06-11 19點23分二十秒’

您所描述的情況,只有當B.NAME是唯一索引的地方,你試圖插入相同值。第一個插入等待A.ID索引被釋放,這將不會因爲B.NAME的值重複而發生。