有问题的Sql Server 2008 R2实例是一个重负载OLTP生产服务器。几天前出现了死锁问题,但仍未解决。我们收到了Xml死锁报告,其中列出了死锁中涉及的存储过程以及其他一些细节。我将首先尝试从这个xml中列出事实:
死锁涉及两个存储过程,例如SP1和SP2。根据报告 SP1在隔离级别“Serializable”中运行, SP2在“ReadCommitted”中运行。
我们调查了以下内容:
我们是否设置了IsolationLevel SP1 SP内部或“内部”中的“可序列化” 码? - 不。
是否有其他SP的IsolationLevel 是“Serializable”调用SP1? - 不。
SP1使用的表是否被调用 任何其他具有隔离功能的SP 级别为“可序列化”? - 是的 有些SP具有隔离功能 级别设置为“可序列化”和 访问与SP1相同的表, 但我们不知道他们是否 当时正在运行 死锁是否僵局 报告仅显示SP1和SP2。
思路:
我们考虑了以下可能的原因:
正在发生死锁,因为SP1是 以“Serializable”运行。 - 为什么是 此SP在Serializable中运行时 我还没设置好吗?是隔离 级别升级(像锁一样)?如果 我们想出来并使其运行 ReadCommitted,问题是什么 解决?
任何其他SP正在运行,锁定 SP1使用的表并导致 SP1和SP2之间的死锁。 - 这个SP不会列在 死锁报告?可以僵局 报告错过这样的依赖?如是 那么我们可能只会偏袒 信息。这仍然没有 解决SP1的运行方式 但可序列化。
建议:
如果此信息不充分 在解决问题时,我该怎么办? 从SQL获取更多信息 服务器为我的目的和什么 我应该尝试收集哪些信息?
你想要的任何其他思路 追求解决这个问题?
更新
这是死锁的跟踪日志信息。我已经更改了SP等的名称,但已经检查并验证了更改不会遗漏任何相关信息。检查代码后面的注释,了解有关表等的更多信息。
?<EVENT_INSTANCE>
<EventType>DEADLOCK_GRAPH</EventType>
<PostTime>2010-09-07T11:27:47.870</PostTime>
<SPID>16</SPID>
<TextData>
<deadlock-list>
<deadlock victim="process5827708">
<process-list>
<process id="process5827708" taskpriority="0" logused="0" waitresource="KEY: 7:72057594228441088 (8d008a861f4f)"
waittime="5190" ownerId="1661518243" transactionname="SELECT" lasttranstarted="2010-09-07T11:27:42.657"
XDES="0x80bf3b50" lockMode="RangeS-S" schedulerid="4" kpid="2228" status="suspended" spid="76" sbid="0"
ecid="0" priority="0" trancount="0" lastbatchstarted="2010-09-07T11:27:42.657"
lastbatchcompleted="2010-09-07T11:27:42.657" clientapp=".Net SqlClient Data Provider"
hostname="xxx" hostpid="5988" loginname="xxx" isolationlevel="serializable (4)"
xactid="1661518243" currentdb="7" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
<executionStack>
<frame procname="SP1" line="12" stmtstart="450" stmtend="6536"
sqlhandle="0x0300070090cbdc7742720c00e99d00000100000000000000">
Select ... from Table1, Table2, Table4, Table5
</frame>
</executionStack>
<inputbuf>
Proc [Database Id = 7 Object Id = 2010958736]
</inputbuf>
</process>
<process id="process5844bc8" taskpriority="0" logused="1873648" waitresource="KEY: 7:72057594228441088 (0e00ce038ed0)"
waittime="4514" ownerId="1661509575" transactionname="user_transaction" lasttranstarted="2010-09-07T11:27:40.423"
XDES="0x37979ae90" lockMode="X" schedulerid="7" kpid="3260" status="suspended" spid="104" sbid="0" ecid="0"
priority="0" trancount="2" lastbatchstarted="2010-09-07T11:27:43.350" lastbatchcompleted="2010-09-07T11:27:43.350"
clientapp=".Net SqlClient Data Provider" hostname="xxx" hostpid="5988" loginname="xxx"
isolationlevel="read committed (2)" xactid="1661509575" currentdb="7" lockTimeout="4294967295"
clientoption1="673185824" clientoption2="128056">
<executionStack>
<frame procname="SP2" line="68" stmtstart="5272" stmtend="5598"
sqlhandle="0x030007003432350f109a0c00e99d00000100000000000000">
UPDATE Table1 ...
</frame>
</executionStack>
<inputbuf>
Proc [Database Id = 7 Object Id = 255144500]
</inputbuf>
</process>
</process-list>
<resource-list>
<keylock hobtid="72057594228441088" dbid="7" objectname="Table1" indexname="Index1"
id="lock448e2c580" mode="X" associatedObjectId="72057594228441088">
<owner-list>
<owner id="process5844bc8" mode="X" />
</owner-list>
<waiter-list>
<waiter id="process5827708" mode="RangeS-S" requestType="wait" />
</waiter-list>
</keylock>
<keylock hobtid="72057594228441088" dbid="7" objectname="Table1" indexname="Index1"
id="lock2ba335880" mode="RangeS-S" associatedObjectId="72057594228441088">
<owner-list>
<owner id="process5827708" mode="RangeS-S" />
</owner-list>
<waiter-list>
<waiter id="process5844bc8" mode="X" requestType="wait" />
</waiter-list>
</keylock>
</resource-list>
</deadlock>
</deadlock-list>
</TextData>
<TransactionID />
<LoginName>xx</LoginName>
<StartTime>2010-09-07T11:27:47.867</StartTime>
<ServerName>xxx</ServerName>
<LoginSid>xxx</LoginSid>
<EventSequence>116538375</EventSequence>
<IsSystem>1</IsSystem>
<SessionLoginName />
</EVENT_INSTANCE>
SP1正在执行一个select,它从5个不同的表(Table1到Table5)中获取数据(使用内部查询等).SP2对Table1执行更新。
一个有趣的事情是SP2更新的一列是Table1中的外键字段和Table2的主键,而Table1和Table2都是SP1的select语句的一部分,不确定这是否相关但不想错过任何东西。
注意:indexname =“Index1”(在上面的死锁图中) - Index1与Table1中的外键和Table2的主键位于同一列上。
答案 0 :(得分:2)
选中MSDN article,其中说明:
隔离级别具有连接范围,并且一旦设置为a 它与SET TRANSACTION ISOLATION LEVEL语句的连接 在连接关闭或其他隔离之前一直有效 等级已设定。当连接关闭并返回池中时, 与上一次SET TRANSACTION ISOLATION LEVEL的隔离级别 声明保留。后续连接重用池 连接使用当时生效的隔离级别 连接汇集在一起。
问题是连接是以Serializable隔离级别打开的;关联的事务已被处置,连接也是如此,但连接没有被销毁并进入连接池。下次发出连接请求(使用相同的连接字符串)时,会返回此连接,并且由于查询未指定任何隔离级别,因此它在Serializable隔离级别中执行。
基本上,如果你有一个连接池并在特定的隔离级别打开一个连接,让我们说Serializable,那么连接将返回到池,隔离级别设置为Serializable。下次请求连接时,您无法确定是否会返回此连接,因此即使通过默认隔离级别进行ReadCommitted,您也可能会获得这些“Serializable”连接之一。
另一个警告是,每次将隔离级别设置为Serializable(或其他任何内容)时,您可能会选择不同的连接,并且通过将其隔离级别设置为Serializable,您可能会慢慢污染连接池中的越来越多的连接(或者你设定的任何东西)。
我没有找到任何重置配置连接的机制(当它在执行我的查询后回到连接池时)。一种解决方法是显式重置每个连接的隔离级别。但这很乏味。
因此,最好的选择是为不同的隔离级别创建单独的连接池。
答案 1 :(得分:1)
在sp1中的那些选定表之后添加(nolock)以确保无法向这些特定表添加读锁定。
答案 2 :(得分:0)
我知道在某些情况下,非聚集索引会导致SELECT
和UPDATE
语句之间出现死锁,听起来这可能与您的情况有关。有关更多信息,请参阅以下链接: