delete,update,select for update如果沒有命中到數(shù)據(jù)產(chǎn)生間隙鎖

具體其他事務(wù)相關(guān)可以看數(shù)據(jù)庫事務(wù)的隔離級(jí)別及常見事務(wù)異常 - 簡(jiǎn)書 (jianshu.com)

update,delete 或者是select for update 如果沒有命中的到數(shù)據(jù)

如果沒有命中到數(shù)據(jù),而使用的條件又是帶有索引的字段,會(huì)使用間隙鎖+臨鍵鎖
并且極其容易產(chǎn)生死鎖!
舉個(gè)例子:
id name other_id node_id
55 啊 201 333
56 哈 210 333
57 哈 211 222
索引為聯(lián)合 索引 other_id , node_id
如上述數(shù)據(jù)使用mysql默認(rèn)可重復(fù)讀隔離級(jí)別,使用如下語句
delete from table where other_id = 205 and node_id = 333
那么 現(xiàn)在獲取排他鎖,沒有匹配到數(shù)據(jù),并且使用索引字段作為條件,那么現(xiàn)在會(huì)利用這個(gè)索引的間隙鎖鎖住區(qū)間數(shù)據(jù),并且產(chǎn)生了臨鍵鎖,遵循左開右閉并且聯(lián)合索引鎖住數(shù)據(jù)
這個(gè)時(shí)候會(huì)通過間隙鎖+臨建鎖,雖然是聯(lián)合索引,但是實(shí)際使用索引左值第一個(gè)other_id來檢測(cè)數(shù)據(jù)鎖的沖突。因?yàn)闆]有具體的數(shù)據(jù)可以排他,間隙鎖大家都嘗試去鎖住一個(gè)區(qū)域,并不會(huì)互相排他等待,但是如果再insert這個(gè)間隙鎖區(qū)域的數(shù)據(jù)時(shí)需要保證排他性。那么這個(gè)時(shí)候才會(huì)去互斥等待,如果前面有兩個(gè)事務(wù)已經(jīng)通過間隙鎖嘗試鎖住這個(gè)區(qū)域了,兩個(gè)事務(wù)會(huì)等待另一個(gè)間隙鎖釋放。則會(huì)產(chǎn)生死鎖
上述delete語句會(huì)獲取(201,210]的other_id的數(shù)據(jù)間隙鎖,但是不會(huì)互斥,那么其他并發(fā)的事務(wù)如果同時(shí)獲取這個(gè)區(qū)間的數(shù)據(jù)也會(huì)獲取間隙鎖,所以間隙鎖是一個(gè)共享鎖。待再insert table into (other_id, node_id) values(205,333) 會(huì)產(chǎn)生死鎖。

具體例子如下

業(yè)務(wù)中我需要將原有的 other_id = 205 and node_id = 333 和 other_id = 206 and node_id = 666
刪除,無論他有木有,再新建

  1. 事務(wù)(1)delete from table where other_id = 205 and node_id = 333
    sleep(8s)
    insert table into (other_id, node_id) values (205,333)
  2. 事務(wù)(2)delete from table where other_id = 207 and node_id = 666
    sleep(8s)
    insert table into (other_id, node_id) values (205,333)
    用兩個(gè)接口測(cè)試,這個(gè)連個(gè)事務(wù)會(huì)死鎖。(sleep是拉長(zhǎng)事務(wù)時(shí)間,提高鎖競(jìng)爭(zhēng)概率,所以說有可能產(chǎn)生死鎖的代碼也是并發(fā)足夠/事務(wù)時(shí)間長(zhǎng)度兩個(gè)因素暴露出來)
    那么如果使用 other_id = 300 則會(huì)獲取 (211, 無窮大 所有大于211的間隙鎖,更容易產(chǎn)生死鎖。
解決方案,如果不是update,并且強(qiáng)制需要原有數(shù)據(jù)的狀態(tài)判斷的依賴,完全可以使用select(不要使用for update)獲取共享鎖,后續(xù)delete,update使用id,如果沒有數(shù)據(jù)也不會(huì)去繼續(xù)操作數(shù)據(jù)庫了,這樣就不會(huì)出現(xiàn)間隙鎖啦!當(dāng)然,如果沒有這個(gè)數(shù)據(jù)你還用一個(gè)沒有的id去delete,還是會(huì)以這個(gè)id生成間隙鎖,但是沒有代碼會(huì)這樣寫吧 - -!,如果select 通過共享鎖事務(wù)拿到數(shù)據(jù),就算其他事務(wù)也同時(shí)進(jìn)行了刪除,那么這個(gè)時(shí)候delete語句就會(huì)有互斥性。并且不會(huì)產(chǎn)生間隙鎖

select * from table where other_id = 205 and node_id = 333
如果不為null 再用 id delete
再 insert table into (other_id, node_id) values (205,333)
select 不會(huì)產(chǎn)生間隙鎖

總結(jié)

一切排他鎖 例如 update, delete 盡量使用 byId一定不會(huì)是間隙鎖。如果是一定需要競(jìng)爭(zhēng)查詢到庫里的數(shù)據(jù),并依賴庫里的數(shù)據(jù)狀態(tài)值,需要用到select for update 盡量保證庫里的數(shù)據(jù)存在,后面再繼續(xù)操作,如果不存在,會(huì)產(chǎn)生間隙鎖,如果繼續(xù)操作極有可能死鎖

如上操作如果你的where條件沒有索引,那就是縮表了。。。百分百死鎖,哈哈哈

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容