2017-04-25 61 views
1

將存儲引擎作爲innodb使用mysql 5.7。我有一張儲存產品信息的表格。該表看起來像這樣具有獨特的鍵上的productId導致死鎖的行上的Mysql併發更新

| Field  | Type   | Null | Key | Default   | Extra      | 
+-----------+--------------+------+-----+-------------------+-----------------------------+ 
| id  | bigint(20) | NO | PRI | NULL    | auto_increment    | 
| productId | varchar(50) | NO | UNI | NULL    |        | 
| seller | varchar(100) | NO | MUL | NULL    |        | 
| updatedAt | timestamp | NO | MUL | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP | 
| status | varchar(100) | NO | MUL | NULL    |        | 
| data  | longtext  | NO |  | NULL    |        | 
+-----------+--------------+------+-----+-------------------+-----------------------------+ 

我通過連接到該MySQL的Java應用程序有兩種操作:
1.新進入的事件(包含關於產品的變更信息)產品ID需要如果它們的版本大於現有事件,則插入它們。該版本作爲json blob存儲在我的數據列中
2.更新productId的行以更改狀態。

我的隔離級別是讀取提交的。 我嘗試了兩種方式,但兩者都導致死鎖:

Approach1:

Transaction1 starts 
Insert ignore into products where productId='X' values(); // Takes a S lock on the row 
select * from products where productId='X' for update ; // Take a X lock on the row to prevent new writes and compare the incoming event with the current event 
Insert into products values on duplicate key update values // insert into row and on duplicate key update values 
commit 

一個併發更新將打開另一個交易:

Transaction2 starts 
select * from products where productId='X' for update ; // Take a X lock on the row to prevent new writes and compare the incoming event with the current event 
Insert into products values on duplicate key update values // insert into row and on duplicate key update values 
commit; 

這是導致死鎖的情況當出現以下情況時:
1.事務1 - 插入忽略語句已對該行進行了S鎖定。
2.事務2 - 選擇更新語句正在等待對行進行X鎖定。
3.事務1 - 選擇更新語句嘗試對行進行X鎖定。

因爲S鎖被保持由事務1和事務2等待採取X鎖,並且當事務1試圖採取X鎖,它會導致死鎖這導致死鎖。

方法2:

Transaction 1 starts: 
select * from products where productId='X' for update ; // If a row exists then it will be locked else I know it does not exist 
Insert ignore into products where productId='X' values(); 
commit 

Transaction 2 starts: 
select * from products where productId='X' for update ; // If a row exists then it will be locked else I know it does not exist 
commit 

這導致了局勢的僵局時,以下幾點:
1.事務1 - 選擇更新語句採用該行的X鎖。
2.事務2 - 選擇更新語句正在等待對該行進行X鎖定。
3.事務1 - 插入忽略語句試圖採取對行的S鎖,但成交1的X鎖已經等待這導致死鎖

因此,一個鎖,我想知道如何處理併發更新並插入新的事件(而不是行更新)到我的表中,而不會導致死鎖。
1.什麼是鎖定順序?
2.如何確保併發更新和新行插入的工作沒有死鎖。

任何幫助,將不勝感激:)

回答

1

我管理的一些實驗後解決它,核心問題是與S的序列,然後在一個事務中,而在另一箇中得到的X鎖攝X鎖。基本上,一開始就採取的S鎖導致所有病例都陷入僵局。
所以,我把insert語句作爲第一條語句移到事務之外。事務現在只需要X個鎖,這意味着其中一個事務在另一個上等待X鎖。

事件1:插入一個新的事件

result = Insert ignore into products where productId='X' values(); 
if result == null 
return 
end 
Transaction start 
select * from products where productId='X' for update ; // Take a X lock on the row to prevent new writes and compare the incoming event with the current event 
Insert into products values on duplicate key update values // insert into row and on duplicate key update values 
commit 

事件2:更新現有的事件

Transaction start 
    select * from products where productId='X' for update ; // If a row exists then it will be locked else I know it does not exist 
    commit 

所以,無論是事件有交易,只有爭奪一個X鎖,這讓我避免死鎖。

+0

或者,重新應用已被殺死的交易。您需要做好準備來做到這一點,因爲某些事情可能導致死鎖或其他對交易致命的錯誤。 –

+0

感謝@RickJames的建議。是的,重新啓動交易是一種選擇。但是,這是一種反覆出現的模式,上述解決方案解決了這個問題。總的來說,你對僵局有何看法,難道不應該避免嗎? –

+0

可以避免一些死鎖。我同意_trying_來避免它們。然而,我發現這個論壇上有很多人似乎認爲所有僵局都可以避免,並花費太多時間來嘗試這樣做。我很高興你解決了你自己的問題。 –