了解 Microsoft SQL-Server 中的六锁

Art*_*dod 5 sql-server deadlock t-sql locking

有人可以解释一下进程如何获取SIX页面上的锁吗?在我的死锁图 xml 文件中,我看到在RC 隔离级别下运行的进程(在死锁时刻执行 select 语句)持有SIX页面上的锁。

这是什么意思以及如何获得该锁?从我从http://msdn.microsoft.com/en-us/library/aa213039%28v=sql.80%29.aspx 获得的信息中,SIX锁保护S所有资源IX上的-locks,并锁定层次结构中较低的一些资源。

对于我的情况,那将是IX行上的锁?可以IX-Lock放在一排?(我觉得不是)。我很迷惑。

另一件事是,我希望X行上有几个-locks 而根本没有S-locks(因为ILReadCommited)。SIX如果我只在前面的语句中插入了几条记录,为什么我锁定了整个页面?

编辑:死锁xml:

<deadlock-list>
 <deadlock victim="process4df94c8">
  <process-list>
   <process id="process4df94c8" taskpriority="0" logused="2968" waitresource="PAGE: 7:1:181357" waittime="3111" ownerId="41854656297" transactionname="user_transaction" lasttranstarted="2013-06-06T11:09:42.087" XDES="0x1d2434e80" lockMode="IX" schedulerid="6" kpid="3476" status="suspended" spid="52" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2013-06-06T11:09:42.183" lastbatchcompleted="2013-06-06T11:09:42.183" clientapp=".Net SqlClient Data Provider" hostname="CWCEINAW" hostpid="4260" loginname="cwcuser" isolationlevel="read committed (2)" xactid="41854656297" currentdb="7" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="adhoc" line="1" stmtstart="508" stmtend="1358" sqlhandle="0x02000000876d6b08786774c344ce9ce8bd077ccce54751be">
INSERT [WP_CashCenter_StockTransactionLine] ([isverified], [isstockownerchanged], [dateupdated], [quantity], [value], [weight], [qualificationtype], [direction], [material_id], [stockcontainer_id], [stockownerid], [stocktransaction_id]) VALUES (@isverified, @isstockownerchanged, @dateupdated, @quantity, @value, @weight, @qualificationtype, @direction, @material_id, @stockcontainer_id, @stockownerid, @stocktransaction_id);     </frame>
     <frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">
unknown     </frame>
    </executionStack>
    <inputbuf>
(@isverified bit,@isstockownerchanged bit,@dateupdated datetime,@quantity int,@value decimal(4,2),@weight decimal(8,8),@qualificationtype int,@direction int,@material_id nvarchar(3),@stockcontainer_id bigint,@stockownerid int,@stocktransaction_id bigint)INSERT [WP_CashCenter_StockTransactionLine] ([isverified], [isstockownerchanged], [dateupdated], [quantity], [value], [weight], [qualificationtype], [direction], [material_id], [stockcontainer_id], [stockownerid], [stocktransaction_id]) VALUES (@isverified, @isstockownerchanged, @dateupdated, @quantity, @value, @weight, @qualificationtype, @direction, @material_id, @stockcontainer_id, @stockownerid, @stocktransaction_id); SELECT scope_identity()    </inputbuf>
   </process>
   <process id="process5cd948" taskpriority="0" logused="24656" waitresource="KEY: 7:72057594277003264 (889d2c878f57)" waittime="3098" ownerId="41854656065" transactionname="user_transaction" lasttranstarted="2013-06-06T11:09:41.970" XDES="0x1253053c0" lockMode="S" schedulerid="3" kpid="4116" status="suspended" spid="59" sbid="0" ecid="0" priority="0" trancount="1" lastbatchstarted="2013-06-06T11:09:42.180" lastbatchcompleted="2013-06-06T11:09:42.177" lastattention="2013-06-03T13:13:45.090" clientapp=".Net SqlClient Data Provider" hostname="CWCEINAW" hostpid="4260" loginname="cwcuser" isolationlevel="read committed (2)" xactid="41854656065" currentdb="7" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="adhoc" line="1" sqlhandle="0x020000009e156e12789f4ef811698c627d479b0240c2a7c1">
SELECT * FROM WP_CashCenter_StockTransaction
                WHERE [id] IN (
                    SELECT DISTINCT [ST].[id]
                    FROM WP_CashCenter_StockTransaction AS [ST]
                        LEFT JOIN WP_CashCenter_StockTransactionLine AS [STL] ON ([STL].[StockTransaction_id] = [ST].[id])
                    WHERE [ST].[Type] IN (1, 0, 10, 9)
AND ([STL].[Direction] IN (1, 0) OR [STL].[id] IS NULL) 
AND [ST].[Status] IN (0, 1)
AND ([STL].[StockContainer_id] = 300000274211 OR [ST].[StockContainerID] = 300000274211))     </frame>
    </executionStack>
    <inputbuf>
SELECT * FROM WP_CashCenter_StockTransaction
                WHERE [id] IN (
                    SELECT DISTINCT [ST].[id]
                    FROM WP_CashCenter_StockTransaction AS [ST]
                        LEFT JOIN WP_CashCenter_StockTransactionLine AS [STL] ON ([STL].[StockTransaction_id] = [ST].[id])
                    WHERE [ST].[Type] IN (1, 0, 10, 9)
AND ([STL].[Direction] IN (1, 0) OR [STL].[id] IS NULL) 
AND [ST].[Status] IN (0, 1)
AND ([STL].[StockContainer_id] = 300000274211 OR [ST].[StockContainerID] = 300000274211))    </inputbuf>
   </process>
  </process-list>
  <resource-list>
   <pagelock fileid="1" pageid="181357" dbid="7" objectname="Eindhoven_CWC.dbo.WP_CashCenter_StockTransactionLine" id="lock366ad3a80" mode="SIX" associatedObjectId="72057594277265408">
    <owner-list>
     <owner id="process5cd948" mode="SIX"/>
    </owner-list>
    <waiter-list>
     <waiter id="process4df94c8" mode="IX" requestType="wait"/>
    </waiter-list>
   </pagelock>
   <keylock hobtid="72057594277003264" dbid="7" objectname="Eindhoven_CWC.dbo.WP_CashCenter_StockTransaction" indexname="PK_WP_Inbound_StockTransaction" id="lockee362e00" mode="X" associatedObjectId="72057594277003264">
    <owner-list>
     <owner id="process4df94c8" mode="X"/>
    </owner-list>
    <waiter-list>
     <waiter id="process5cd948" mode="S" requestType="wait"/>
    </waiter-list>
   </keylock>
  </resource-list>
 </deadlock>
</deadlock-list>
Run Code Online (Sandbox Code Playgroud)

Seb*_*ine 17

为了回答这个问题,我不得不绕道而行,所以请耐心等待。

如果两个会话锁定同一资源,SQL Server 会检查锁兼容性映射,如果第二个请求与第一个请求“不兼容”,则第二个会话必须等待。共有三种锁类型“S”shared、“U”pdate 和 e“X”clusive。S 锁用于读取资源,而 X 锁用于写入资源。S 锁相互兼容,X 锁不兼容其他任何东西。U 锁是一种混合,在某些情况下用于防止死锁。

现在,SQL Server 可以在多个级别上锁定:表、分区、页和行。因此,如果会话 1 使用表锁而会话 2 在表的一行上使用不兼容的锁,则这两个锁不在同一资源上,SQL Server 将不会检测到冲突。为了防止这种情况发生,SQL Server 总是开始在表级别上锁定并在层次结构中向下工作。现在页锁和行锁的重点是更高的并发性,所以如果一个会话想写一行,另一个会话想写另一行,他们不应该互相阻塞。如果一个会话除了对行加锁外,还必须对表加锁,那么这个优势就没有了。因此,会话不会在表上获取排他锁 (X),而是请求一个意向排他锁 (IX)。此锁与其他意向锁兼容,但与其他“真实”锁不兼容。因此,另一个会话也可以在同一个表上使用意向排他锁。意图排他锁表示会话打算对较低级别的资源进行排他锁。在页面级别也会发生同样的情况,如果预期的锁是行锁,那么在完成所有操作之后,会话在表和其中一个页面上有一个 IX 锁,在该页面中的一个行上有一个 X 锁. 这也意味着,您永远不会在行上找到意向锁,因为行是锁层次结构中的最低级别。在页面级别也会发生同样的情况,如果预期的锁是行锁,那么在完成所有操作之后,会话在表和其中一个页面上有一个 IX 锁,在该页面中的一个行上有一个 X 锁. 这也意味着,您永远不会在行上找到意向锁,因为行是锁层次结构中的最低级别。在页面级别也会发生同样的情况,如果预期的锁是行锁,那么在完成所有操作之后,会话在表和其中一个页面上有一个 IX 锁,在该页面中的一个行上有一个 X 锁. 这也意味着,您永远不会在行上找到意向锁,因为行是锁层次结构中的最低级别。

在某些情况下,会话在表或页上持有 S 锁。如果会话现在(在同一个事务中)在同一个表中的行上请求 X 锁,它首先必须在表/页上获取 IX 锁。但是,一个会话在任何给定资源上只能持有一个锁。因此,要获得 IX 锁,就必须释放可能不需要的 S 锁,因此 SQL Server 提供了一个组合:SIX。

之所以有页面锁,是因为 SQL Server 有时会决定最好锁定页面而不是锁定每一行。如果在所有会话之间已经有很多锁,这种情况经常发生,但也可能有许多其他原因。

理论到此为止。

现在,在您的情况下,六锁由三表连接选择查询持有。除非您明确告诉它(例如,使用 XLOCK 提示),否则 select 永远不会接受任何类型的不是共享锁的锁。这样的提示在输入缓冲区中是不可见的,因此我假设 IX 部分是此连接上最后一批的剩余部分。如果您正在使用连接池并且忘记清理所有打开的事务,那么这样的锁可能会永远存在。但它也变得很难排除故障。

您可以首先运行将 OPEN TRAN 与 COMMIT 配对的 XEvent 会话,看看您是否可以通过这种方式找到罪魁祸首。