Ami*_*har 4 sql-server-2008 sql-server deadlock transaction
如果有人可以帮助我理解为什么以下交易陷入僵局?我为每个表提供 2 个事务和索引:
交易1:
SELECT blockId, blockData, syncedToGeneration, itemId
FROM blocks
WHERE indexId=@indexId
and itemId IN (SELECT itemId
FROM KeywordReferences
WHERE indexId=@indexId
AND keywordRootId IN (360,4498,359,1229))
Run Code Online (Sandbox Code Playgroud)
交易2:
UPDATE blocks
SET blockData=@blockData,
syncedToGeneration=@syncedToGeneration
WHERE indexId=@indexId
AND blockId=@blockId
Run Code Online (Sandbox Code Playgroud)
(请注意,第一笔交易中的“IN”部分要长得多,包含大约 30 个值,为了便于阅读,我将其截断了)
blocks 表有以下索引:
- indexId->blockId (Clustered)
- indexId->itemId
indexId->itemId
keywordReferences 表具有以下索引:
- indexId_>keywordRootId (Clustered)
- indexId->keywordRootId->score
- indexId->itemId
- indexId->blockId
波纹管是死锁图xml输出:
<deadlock-list>
<deadlock victim="process5a274c8">
<process-list>
<process id="process5a274c8" taskpriority="0" logused="0" waitresource="PAGE: 5:1:91671" waittime="2709" ownerId="122348" transactionname="SELECT" lasttranstarted="2012-04-19T21:23:26.680" XDES="0xf382aca0" lockMode="S" schedulerid="4" kpid="10992" status="suspended" spid="69" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2012-04-19T21:23:26.650" lastbatchcompleted="2012-04-19T21:23:26.647" clientapp=".Net SqlClient Data Provider" hostname="AMIT-PC" hostpid="6752" loginname="sa" isolationlevel="read committed (2)" xactid="122348" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="adhoc" line="1" stmtstart="34" sqlhandle="0x020000002e9a3633b816ffc89dc234b4c0351887e4e1b2cf">
SELECT blockId, blockData, syncedToGeneration, itemId FROM blocks WHERE indexId=@indexId and itemId IN (SELECT itemId FROM KeywordReferences WHERE indexId=@indexId AND keywordRootId IN (360,4498,359,1229,2143,14330,7661,3755,1156,21490,5567,1933,429,28197,2,3165,524,3182,2655,27262,17407,2673,570,1478,3802,6838,19668,17,6586,2484,2794,1640,5171,2558,6592,5833,695,1199,2307,335,1351,6651,6899,3740,7048,22030,14356,597,3175,3965,3297,2711,14484,2761,2265,28,1647,3223,226,304,298,1157,197,2696,21172,19149,9,1159,135,1,3166,23325)) </frame>
<frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
(@indexId bigint)SELECT blockId, blockData, syncedToGeneration, itemId FROM blocks WHERE indexId=@indexId and itemId IN (SELECT itemId FROM KeywordReferences WHERE indexId=@indexId AND keywordRootId IN (360,4498,359,1229,2143,14330,7661,3755,1156,21490,5567,1933,429,28197,2,3165,524,3182,2655,27262,17407,2673,570,1478,3802,6838,19668,17,6586,2484,2794,1640,5171,2558,6592,5833,695,1199,2307,335,1351,6651,6899,3740,7048,22030,14356,597,3175,3965,3297,2711,14484,2761,2265,28,1647,3223,226,304,298,1157,197,2696,21172,19149,9,1159,135,1,3166,23325)) </inputbuf>
</process>
<process id="process5a13b88" taskpriority="0" logused="215304" waitresource="PAGE: 5:1:91669" waittime="2910" ownerId="128212" transactionname="user_transaction" lasttranstarted="2012-04-19T21:23:28.567" XDES="0xedcd9000" lockMode="IX" schedulerid="2" kpid="5500" status="suspended" spid="68" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2012-04-19T21:23:29.007" lastbatchcompleted="2012-04-19T21:23:29.007" clientapp=".Net SqlClient Data Provider" hostname="AMIT-PC" hostpid="6752" loginname="sa" isolationlevel="read committed (2)" xactid="128212" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="adhoc" line="1" stmtstart="154" sqlhandle="0x02000000f4d83b0df2bfedc5a346288c21fa78e07eb152f6">
UPDATE blocks SET blockData=@blockData, syncedToGeneration=@syncedToGeneration WHERE indexId=@indexId AND blockId=@blockId </frame>
<frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
(@indexId bigint,@blockId int,@blockData ntext,@syncedToGeneration int)UPDATE blocks SET blockData=@blockData, syncedToGeneration=@syncedToGeneration WHERE indexId=@indexId AND blockId=@blockId </inputbuf>
</process>
</process-list>
<resource-list>
<pagelock fileid="1" pageid="91671" dbid="5" objectname="isqdb.dbo.blocks" id="lock5c54700" mode="IX" associatedObjectId="72057594043826176">
<owner-list>
<owner id="process5a13b88" mode="IX"/>
</owner-list>
<waiter-list>
<waiter id="process5a274c8" mode="S" requestType="wait"/>
</waiter-list>
</pagelock>
<pagelock fileid="1" pageid="91669" dbid="5" objectname="isqdb.dbo.blocks" id="lock5b84780" mode="S" associatedObjectId="72057594043826176">
<owner-list>
<owner id="process5a274c8" mode="S"/>
</owner-list>
<waiter-list>
<waiter id="process5a13b88" mode="IX" requestType="wait"/>
</waiter-list>
</pagelock>
</resource-list>
</deadlock>
</deadlock-list>
Run Code Online (Sandbox Code Playgroud)
这看起来像是表扫描的典型案例(可能缺少索引,但可能更多)。SELECT 选择了一个页锁粒度,指示大扫描(在blocks表上)。还要注意所有锁是如何在同一个资源行集上的,聚集索引,另一个表明 SELECT 不使用选择性二级索引来定位行。您似乎希望 上的非聚集索引(indexId, itemId)(您将其描述为存在)会被选中,而且如果选中它,查询就不会死锁。就目前情况而言,由于选择性(它达到了临界点),它看起来很可能被忽略了。在不了解您的工作负载和数据模型的情况下,很难为更好的索引提供建议。至少我可以说对项目的要求blockData, syncedToGeneration在 SELECT 列表中使索引不覆盖,因此也许将它们添加为包含的列将是第一步。但是,如果不知道这些列的大小(blockData似乎是一个大列的名称......),再次很难预测结果。
为了举例说明为什么扫描是罪魁祸首,请考虑由 UPDATE 语句更新的任意两行。UPDATE 将按顺序更新它们,(indexId, blockId)但 SELECT 扫描将按顺序查看它们(indexId, itemId)。除非在itemId和之间存在函数依赖关系,blockId以保证两个索引键以相同的顺序返回所有行,否则总会有一些行对在聚集索引和非聚集索引之间的顺序颠倒。它的要点是,因为 SELECT 进行扫描,所以保证访问每一行。因此,任何由 UPDATE 更新的行都将被扫描访问。有四种可能:
在存在扫描的情况下,这种情况总是会随着多行更新而出现。解决方案通常是用更具选择性的操作替换扫描,这具有提高性能的额外好处。如果 SELECT 和 UPDATE 只访问他们真正想要处理的行,那么只有在同一逻辑数据上同时发生两个操作时才会发生死锁(即,同一项目同时被读取和更新),但大多数情况下在 OLTP 系统上,这被业务工作流阻止。它仍然可能发生,但频率大大降低。附带说明一下,即使在对不相交的逻辑项进行操作时,由于哈希冲突,它仍然是概率游戏。
事实上,一种简单的方法是部署快照隔离。但是潜在的问题(扫描)只会被掩盖,而不会得到缓解。扫描引起的其他问题(性能差、高延迟和响应时间慢、缓冲池污染等)仍然存在。您应该修复扫描问题并部署快照隔离。
PS 请注意,扫描使用 PAGE 粒度锁这一事实无关紧要。如果扫描使用 ROW 粒度,死锁只会发生在行而不是页上。但是选择 PAGE 粒度这一事实对于扫描来说是很重要的。
| 归档时间: |
|
| 查看次数: |
7179 次 |
| 最近记录: |