sql server update與delete引發的死鎖


【1】死鎖發生及基本信息

死鎖問題,想不明白為什么會死鎖,求大佬分析詳細原因和加鎖、等待之類的詳細過程過程,以便理解 解決
信息如下: 

【1.1】被死鎖的基本信息

tOnlineUser 死鎖發生表的索引信息:
名稱:IX_tOnlineUser
類型:nonclustered, ignore duplicate keys, unique located on PRIMARY
索引列:iUserID


【1.2】死鎖圖片與死鎖代碼信息



--SP1  被犧牲(圖中左邊)
SELECT @iDbGsID=iGameServerID,@bDbState=bState FROM tOnlineUser WHERE iUserID = @iUserID  
UPDATE tOnlineUser SET bState=3 WHERE iUserID = @iUserID  
  
  
 --SP2 (圖中右邊)
DELETE FROM tOnlineUser WHERE iUserID=@iUserID  
INSERT tOnlineUser (iUserID,iGameServerID,bState) VALUES (@iUserID,@iGsID,@bOnlineState)  

【1.3】死鎖XML信息

Deadlock graph    <deadlock-list>
 <deadlock victim="process47704d8">
  <process-list>
   <process id="processff12e8" taskpriority="0" logused="84" waitresource="KEY: 14:72057594038976512 (9e00c0f50f63)" waittime="3187" ownerId="862602370" transactionname="DELETE" lasttranstarted="2020-09-24T20:46:44.200" XDES="0xffffffff80048380" lockMode="X" schedulerid="7" kpid="1236" status="suspended" spid="71" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2020-09-24T20:46:44.200" lastbatchcompleted="2020-09-24T20:46:44.200" hostname="BOX-5" hostpid="1932" loginname="BOX_User" isolationlevel="read committed (2)" xactid="862602370" currentdb="14" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
    <executionStack>
     <frame procname="BOX_RunCenter.dbo.me_UserOnlineState" line="35" stmtstart="890" stmtend="1014" sqlhandle="0x03000e005cbf2019d1d51601ada700000100000000000000">
DELETE FROM tOnlineUser WHERE iUserID=@iUserID

    --在線 或 掉線     </frame>
    </executionStack>
    <inputbuf>
Proc [Database Id = 14 Object Id = 421576540]    </inputbuf>
   </process>
   <process id="process47704d8" taskpriority="0" logused="0" waitresource="RID: 14:1:185:196" waittime="3187" ownerId="862602372" transactionname="SELECT" lasttranstarted="2020-09-24T20:46:44.200" XDES="0xffffffff9f80a7b0" lockMode="S" schedulerid="15" kpid="8704" status="suspended" spid="129" sbid="0" ecid="0" priority="0" transcount="0" lastbatchstarted="2020-09-24T20:46:44.200" lastbatchcompleted="2020-09-24T20:46:44.200" hostname="iZjcdsetuetu8jZ" hostpid="692" loginname="BOX_User" isolationlevel="read committed (2)" xactid="862602372" currentdb="14" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
    <executionStack>
     <frame procname="BOX_RunCenter.dbo.me_GetUserOnlineServerID" line="39" stmtstart="1206" stmtend="1404" sqlhandle="0x03000e00b1524416c8d51601ada700000100000000000000">
SELECT @iGsID = iGameServerID,@bState=bState FROM tOnlineUser WHERE iUserID = @iUserID

    --沒有記錄     </frame>
    </executionStack>
    <inputbuf>
Proc [Database Id = 14 Object Id = 373576369]    </inputbuf>
   </process>
  </process-list>
  <resource-list>
   <keylock hobtid="72057594038976512" dbid="14" objectname="BOX_RunCenter.dbo.tOnlineUser" indexname="IX_tOnlineUser" id="lockffffffffd7158ac0" mode="U" associatedObjectId="72057594038976512">
    <owner-list>
     <owner id="process47704d8" mode="S"/>
    </owner-list>
    <waiter-list>
     <waiter id="processff12e8" mode="X" requestType="convert"/>
    </waiter-list>
   </keylock>
   <ridlock fileid="1" pageid="185" dbid="14" objectname="BOX_RunCenter.dbo.tOnlineUser" id="lockfffffffffd721240" mode="X" associatedObjectId="72057594038910976">
    <owner-list>
     <owner id="processff12e8" mode="X"/>
    </owner-list>
    <waiter-list>
     <waiter id="process47704d8" mode="S" requestType="wait"/>
    </waiter-list>
   </ridlock>
  </resource-list>
 </deadlock>
</deadlock-list>
    

【2】分析

【2.1】加鎖過程分析

(1)看你的死鎖示意圖,SP1應該是在執行update,update會在涉及到的鍵列上先放置U鎖,然后通過where條件是定位行。

(2)但是這個時候SP2的delete已經在定位到的行上放置了X鎖,然后就死鎖了

(3)為什么SP2 還需要鍵鎖的X呢?因為已經確定刪除行了,那么對應的索引鍵值也要刪除掉。

總結就是:

  我們都知道update和delete 等操作,是需要先查詢,然后再進行操作的,那么總結核心過程應該如下:

(1)SP1的 update和 sp2 delete同時運行針對同一個索引IUserID 值,先同時獲取到索引的S鎖用來查詢

(2)然后SP2的 delete 先一步獲取到了對應行的 RID鎖(行鎖),它操作刪除行。而此時SP1在等待SP2釋放該索引鍵值IUserID對應的 RID頁鎖。

(3)這個時候SP2 的 delete 刪除完行數據后,想要獲取 IUserID 的鍵鎖,因為是要把對應的索引鍵值一起刪掉。但這個時候,索引鍵值的 S鎖 還持有在 SP1的 update上。

(4)最終, SP1的 update 擁有IUserID 的索引鍵值鎖(S),SP2的 delete 有用 IUserID 所對應行的 RID行鎖(X),他們互相需要對方的資源鎖,然后又互相等待,形成了死鎖的循環等待。

【3】解決辦法

(1)UPDLOCK

最終使用了這種辦法解決

指定采用更新鎖並保持到事務完成。 UPDLOCK 僅對行級別或頁級別的讀操作采用更新鎖。 

如果將 UPDLOCK 與 TABLOCK 組合使用或出於一些其他原因采用表級鎖,將采用排他 (X) 鎖。

SELECT @iDbGsID=iGameServerID,@bDbState=bState 
FROM tOnlineUser with(updlock) WHERE iUserID = @iUserID

(2)消除額外的鍵查找鎖需的鎖

直接在 iUserID 上加上 聚集索引,不過要是大表 代價太大了,很影響性能,不太可取

(3)讀操作時取消獲取鎖

使用 with nolock 、或者切換快照、讀已提交快照隔離級別解決。

 

 

 

 

 

【參考文檔】

【0】必看參考:SQL SERVER - 談死鎖的監控分析解決思路https://www.cnblogs.com/xinysu/p/6511360.html

【1】select 與 update 的死鎖:https://blog.csdn.net/ajianchina/article/details/46807131

【2】高並發select 與 update引起的死鎖:https://blog.csdn.net/weixin_44774463/article/details/108204456

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM