重複插入相同數據導致deadlock問題:Deadlock found when trying to get lock; try restarting transaction

場景:

業務邏輯:第三方登錄情況下,獲取到用戶的實名信息。之後判斷該用戶在用戶表中是否存在,如果不存在或非實名,那麼將其實名;如果已經實名,那麼不做處理,直接登錄。ORM使用的是spring data jpa,用戶表在mobile字段上有唯一索引idx_mobile

發現不定期的發生業務報錯:Deadlock found when trying to get lock; try restarting transaction

原因:

分析死鎖日誌

通過SHOW ENGINE INNODB STATUS;來查看死鎖日誌:

日誌類似

注意:SHOW ENGINE INNODB STATUS\G 看到的DEADLOCK相關信息,只會返回最後的2個事務的信息,而其實有可能有更多的事務才最終導致的死鎖

日誌的上半部分說明事務1在等待什麼鎖

ip1 dbuser update

這個用戶在執行下面這條sql語句

insert into 用戶表 值1

其在申請idx_mobile索引的

RECORD LOCKS space id 3251 page no 14336 n bits 704 index `idx_mobile` of table 用戶表 trx id 306872608 lock_mode X locks gap before rec insert intention waiting

這條插入記錄的事務等待中,等待獲得插入意向鎖

 

日誌的下半部分說明了事務2當前持有的鎖以及等待的鎖:

ip2 dbuser update

這個用戶在執行下面這條sql語句

insert into 用戶表 也是值1

HOLDS THE LOCK(S):

事務2持有S gap lock

lock mode S locks gap before rec

至於爲什麼加S Gap-Lock ,是因爲在插入之前還需要多一步檢查:如果記錄中有唯一約束,判斷存在一條記錄等於當前插入的記錄時,則需要在這個記錄加上S Gap-Lock

也就是說事務1的insert intention lock等待事務2的s gap-lock釋放

從日誌的WAITING FOR THIS LOCK TO BE GRANTED塊中我們可以看到事務2正在申請插入意向鎖

那是什麼原因造成這個dead lock呢?

事務0的回滾導致事務1和事務2的deadlock

爲什麼是事務0呢,看後面的參考就知道了,事務1和事務2的死鎖是由於事務0rollback導致的

參考:

併發insert操作導致的dead lock

還原整個過程

第三方登錄的情況下,前後端沒有做重複提交的避免策略,這樣會造成一個用戶可以多次執行第三方登錄的請求,當用戶短時間內連續3次(或以上)執行第三方登錄的請求,導致會起3個transaction(事務0 事務1 事務2)去執行insert操作

此時如果事務0由於業務代碼問題rollback,會導致事務1和事務2 deadlock,直到mysql鎖超時,報deadlock錯誤

即發生:當有3個(或以上)事務對相同的表進行insert操作,如果insert對應的字段上有uniq key約束並且第一個事務rollback了,那其中一個將返回死鎖錯誤信息。

解決方案

避免此DEADLOCK;我們都知道死鎖的問題通常都是業務處理的邏輯造成的,既然是uniq key,同時多臺不同服務器上的相同程序對其insert一模一樣的value,這本身邏輯就不太完美。故解決此問題:

思路1:

保證業務程序別在同一時間點併發的插入相同的值到相同的uniq key的表中

前端可以通過

1.提交數據之前判斷當前提交按鈕是否存在lock鎖

2.在ajax提交之前給提交按鈕上鎖

3.ajax成功之後或者失敗之後解鎖

後端可以通過redis+aop來做

參考:redis防表單重複提交

思路2:

由於是事務0 rollback了才產生的deadlock,查明rollback的原因

 

我們的解決方法

我們現在是前端做重複提交的去重。

後端修改了可能產生rollback的邏輯

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章