2014-07-18 41 views
3

我在中等到高負載情況下遇到死鎖。這裏是細節。Mysql在插入和更新時出現死鎖

的MySQL-5.5.21-55

引擎:InnoDB的

表:訂單

# Field, Type, Null, Key, Default, Extra 
id, bigint(20) unsigned, NO, PRI, , auto_increment 
sno, varchar(32), NO, MUL, , 
misc1, int, NO, , 0, 

表:OrderItem的

# Field, Type, Null, Key, Default, Extra 
id, bigint(20) unsigned, NO, PRI, , auto_increment 
order_id, bigint(20), YES, MUL, , 
f1, varchar(50), YES, , , 
f2, varchar(100), YES, , , 
misc2, int, NO, , 0, 
  • 訂購 .sno是唯一
  • OrderItem的 .order_id沒有被定義爲外鍵,但用作應用外鍵
  • 訂購具有一個一對多關係與OrderItem的
  • OrderItem的 .order_id + OrderItem的 .f1 + OrderItem的 .f2是唯一

USECASE:

  • 每當訂單OrderItem的需要更新任何記錄,我要廢止舊記錄(或刪除),並插入新的。
  • 可能因此發生較早,在訂購一個記錄(例如order1)有3條記錄OrderItem的(例如orderItem1,orderItem2,orderItem3)。但現在我想把它作爲order1-> orderItem1,orderItem4,orderItem5或全​​新的集合。這就是爲什麼我想要將舊記錄全部無效並插入新記錄以查明訂單項變得複雜的原因。

多線程會做這個操作;但他們會在不同的記錄集上工作。我在一個時間

我試了一下在25 訂單經營:

  • 插入訂單;重複密鑰更新訂購並從訂單項刪除所有子項並插入訂單項 s。
  • 呼籲IS_ACTIVE訂單並標記爲同一SNO的所有記錄爲0,並在秩序插入新記錄的另一列;將新的子女插入訂單項
  • 刪除訂單給定的sno;從刪除OrderItem爲同一個sno.Insert插入兩個表中新鮮。

以上所有方法都會導致無法鎖定,有些時候或其他時間。 沒有其他線程或進程正在處理這些表。

觀察:

通過以下鏈接

去了,發現更新/刪除多個記錄導致MySQL獲得下一個關鍵鎖定一個t REPEATABLE_READ隔離級別(這是默認級別)。在我看來這導致了這個問題。

感謝您能否提供解決方案。

+0

鎖只鎖定其他連接,不能在同一數據庫連接上的操作之間發生死鎖。 – Barmar

+0

「新訂單」動作如何觸發...?一個簡單的http請求? –

+0

@Barmar連接不被線程共享。 –

回答

0

最後,(或許是一種解決方法,在網上與@ JackyCheng的建議),IM,

  • 插入到訂單表(同時多條記錄); 重複密鑰沒有更新
  • 插入到OrderItem表(一次多個記錄);
  • 選擇所有訂購 .ids將被更新
  • 標記標識訂單(IDS)爲非活動一個接一個(這是關鍵部分,因爲它使用了索引鍵鎖,而不是下鍵鎖)

總體交易執行時間仍低於30毫秒的25份訂單。

1

既然你沒有提到它,我會試試看,你有沒有試過「FOR UPDATE」?

將連接自動提交設置爲false。

在程序開始時,用這個來鎖定所有相關的數據。

SELECT o.* , oi.* 
FROM order o 
INNER JOIN orderitem oi ON (o.id=oi.order_id) 
WHERE o.id = <order id to update> 
FOR UPDATE; 

然後你可以做任何你想要的那些條目。 然後提交。我認爲問題的根本原因是多線程(顯然),我想如果從等式中刪除該元素更好。

想象一個系統,您有多個接收器接收請求。這些接收器只會做這樣的事情:

//select to check if there is a existing record(no need to lock), if no, return fail as response 
SELECT o.* , oi.* 
FROM order o 
INNER JOIN orderitem oi ON (o.id=oi.order_id) 
WHERE o.sno = <sno to update>; 

insert into request_buffer (request_id, sno, new_order_item,create_date .......) 
values 
(1, abc , orderitem1......); 
//return success after inserting buffer. 

,你有池此表一個單獨的單線程程序和處理這些緩衝區條目。

在這種情況下,它會從數據庫更新過程中分離出多線程元素。我不確定收到的請求數量,但我認爲如果您在每個週期/查詢中處理更多的請求,那麼性能不會有顯着差異?

+0

我把這作爲最後的手段;即一次執行一個記錄的刪除/更新操作。當我在where子句中使用範圍,即IN子句時,它使用NEXT KEY鎖,這會導致死鎖。 –

+0

@PragalathanM稍微修改了第二種方法。接收器 –

+0

增加檢查問題是負載。我們希望有多臺服務器處理這些記錄。記錄每小時大約100萬次。所以要麼我們必須在同一臺服務器或多臺服務器上有多個線程。 –