高并發(fā)狀態(tài)下Replace Into造成的死鎖問題解決
目錄
- 1.問題出現(xiàn):
- 2.分析解決
- 3.解決方案:
1.問題出現(xiàn):
在測(cè)試階段,大數(shù)據(jù)并發(fā)的情況下,發(fā)現(xiàn)sql語句造成表的死鎖,過一段時(shí)間,死鎖消失。于是進(jìn)行排查
報(bào)錯(cuò)如下:
對(duì)應(yīng)的sql語句如下:
@Insert("replace into ${tableName}( windcode,date, \n" + " code, high, open, low, \n" + " `close`, volume, turnover,gtm_modify) " + "values (#{obj.windcode},#{obj.date},#{obj.code},#{obj.high},#{obj.open},#{obj.low},#{obj.close},#{obj.volume},#{obj.turnover},#{obj.updateTime})" ) int insertOne(@Param("obj") KDTO obj, @Param("tableName") String tableName);
在排除了數(shù)據(jù)問題和線程重復(fù)調(diào)用以后,我們關(guān)注了一下sql語句本身。 看了網(wǎng)上很多經(jīng)驗(yàn)分享,覺得問題可能出現(xiàn)在 Replace Into 語句上。
2.分析解決
首先我們分析一下為什么并發(fā)replace into導(dǎo)致MySQL死鎖
Replace into 一般作用是,當(dāng)存在沖突時(shí),會(huì)把舊記錄替換成新的記錄。也就是說這條語句執(zhí)行,分為了兩個(gè)大步:判斷和執(zhí)行
1.判斷:
首先判斷我們需要操作的記錄是否存在(根據(jù)主鍵或者唯一索引判斷)
2.操作:
- 針對(duì)不存在的記錄,語句會(huì)執(zhí)行insert,插入操作。
- 針對(duì)已經(jīng)存在的記錄,語句可以拆分為delete+insert操作
測(cè)試:
建立表
插入數(shù)據(jù):
我們使用replace into語句去執(zhí)行一個(gè)已經(jīng)存在的數(shù)據(jù):
可以清楚的發(fā)現(xiàn),影響的行數(shù)是兩行
第一行的數(shù)據(jù)被修改了
我們使用replace into語句去執(zhí)行一個(gè)不存在的數(shù)據(jù):
可以清楚的發(fā)現(xiàn),影響的行數(shù)是一行
執(zhí)行了插入操作:
邏輯非常的清晰,但是這種單條sql語句在什么情況下會(huì)出現(xiàn)死鎖呢?我們就要去考慮這個(gè)加鎖的時(shí)機(jī)。
正常的插入邏輯是:
- 首先插入聚集索引記錄,在上例中id列為自增列。
- 隨后插入二級(jí)索引num,由于其是唯一索引,在檢查duplicate key時(shí),為其加上類型為LOCK_X的記錄鎖。
發(fā)現(xiàn)錯(cuò)誤:
- 由于檢測(cè)到duplicate key,因此第一步插入的聚集索引記錄需要被回滾掉(row_undo_ins)。
- 從InnoDB層失敗返回到Server層后,收到duplicate key錯(cuò)誤,首先檢索唯一鍵沖突的索引,并對(duì)沖突的索引記錄(及聚集索引記錄)加鎖。
轉(zhuǎn)換模式:
如果發(fā)生uk沖突的索引是最后一個(gè)唯一索引、沒有外鍵引用、且不存在delete trigger時(shí),使用UPDATE ROW的方式來解決沖突;
否則,使用DELETE ROW + INSERT ROW的方式解決沖突。
更新記錄:
- 對(duì)于聚集索引,由于PK列發(fā)生變化,采用delete + insert 聚集索引記錄的方式更新。
- 對(duì)于二級(jí)uk索引,同樣采用標(biāo)記刪除 + 插入的方式。
所以死鎖的問題多半就會(huì)出現(xiàn)在X記錄鎖上面。
死鎖分析:
所以再多線程高并發(fā)的環(huán)境狀態(tài)下,存在兩個(gè)事務(wù)同時(shí)去獲取一個(gè)記錄的修改的情況:
- 事務(wù)1拿到X記錄鎖,
- 事務(wù)2檢測(cè)到?jīng)_突,獲取X|NK鎖,被事務(wù)1阻塞
- 事務(wù)1檢測(cè)到?jīng)_突,申請(qǐng)獲取S|NK,被事務(wù)2阻塞
所以在等待執(zhí)行期間sql會(huì)有死鎖報(bào)錯(cuò),高并發(fā)環(huán)境下的死鎖也就出現(xiàn)了,再事務(wù)執(zhí)行完成回滾操作以后,死鎖回滾,也就解釋了死鎖消失的問題。
3.解決方案:
經(jīng)過多方討論,最終決定使用 insetr + ON DUPLICATE KEY UPDATE語句替換高并發(fā)環(huán)境下的Replace Into語句解決死鎖問題。
ON DUPLICATE KEY UPDATE語句的作用是:
若該數(shù)據(jù)的主鍵值/ UNIQUE KEY 已經(jīng)在表中存在,則執(zhí)行更新操作, 即UPDATE 后面的操作。
否則插入一條新的記錄。
實(shí)現(xiàn)了Replace Into有相同的查重替換功能,而避免了高并發(fā)的死鎖問題。
但是UPDATE操作性能相比DELETE操作會(huì)有一定的性能上的影響,需要后續(xù)測(cè)試跟進(jìn)。
到此這篇關(guān)于高并發(fā)狀態(tài)下Replace Into造成的死鎖問題解決的文章就介紹到這了,更多相關(guān)Replace Into死鎖內(nèi)容請(qǐng)搜索以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持!
相關(guān)文章:
