• 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 、或者切换快照、读已提交快照隔离级别解决。

     

    【参考文档】

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

    【2】高并发select 与 update引起的死锁:https://blog.csdn.net/weixin_44774463/article/details/108204456

  • 相关阅读:
    jQuery入门和DOM对象
    jQuery事件
    基础,层次,选择器
    MarkDown快速入门(typora)
    source是读入环境配置文件的命令,不能读入vimrc
    vi中将tab键转化为空格
    django-rest-framework学习之Quickstart和Serializer--2017年4月10日至12日
    Flask-RESTful插件介绍--2017年4月7日
    python restful api 编程--2017年4月6日
    一个验证登录的程序:python编写flask架构restful风格--2017年4月6日
  • 原文地址:https://www.cnblogs.com/gered/p/13793137.html
Copyright © 2020-2023  润新知