SQL死锁与表上的选择/更新操作

时间:2017-11-14 13:52:49

标签: sql-server sql-server-2008 sql-server-2012

我已将列(prod_id)数据类型“nvarchar(25)”更改为“varchar(50)”,然后并行DML操作失败并出现死锁。

涉及死锁的表 - “tbl_Ref_Attr_Prod_Team” 表上的索引

Indexes on table

这是select / update deadlock xml ..

<deadlock>
 <victim-list>
  <victimProcess id="process3980de4558" />
 </victim-list>
 <process-list>
  <process id="process3980de4558" taskpriority="0" logused="0" waitresource="PAGE: 7:1:1660625 " waittime="966" ownerId="635070687" transactionname="SELECT" lasttranstarted="2017-11-14T04:09:08.120" XDES="0x19f7d80040" lockMode="IS" schedulerid="6" kpid="23696" status="suspended" spid="121" sbid="0" ecid="1" priority="0" trancount="0" lastbatchstarted="2017-11-14T02:12:22.280" lastbatchcompleted="2017-11-14T02:12:22.280" lastattention="1900-01-01T00:00:00.280" clientapp=".Net SqlClient Data Provider" hostname="GADC-WMGXSQLP01" hostpid="16128" isolationlevel="read committed (2)" xactid="635070687" currentdb="7" lockTimeout="4294967295" clientoption1="671088928" clientoption2="119832">
   <executionStack>
    <frame procname="adhoc" line="1" stmtstart="112" sqlhandle="0x020000008925110aeb7cb1c1c073dab88fd24fca1951ea9d0000000000000000000000000000000000000000">
select @existing = team_it_cube_attr_05 from tbl_Ref_Attr_Prod_Team where prod_id = @rec_key    </frame>
    <frame procname="mssqlsystemresource.sys.sp_executesql" line="1" stmtstart="-1" sqlhandle="0x0400ff7f427f99d9010000000000000000000000000000000000000000000000000000000000000000000000">
sp_executesql    </frame>
    <frame procname="WMGDS_Ref.dbo.sp_datman_get_attr_value" line="30" stmtstart="2266" stmtend="2438" sqlhandle="0x03000700b694bf1125f34901f9a1000001000000000000000000000000000000000000000000000000000000">
EXECUTE @return_code = sp_executesql @SQL, @ParamDefinition, @item_key, @value OUTPUT    </frame>
    <frame procname="WMGDS_Ref.dbo.sp_datman_validate_category_role" line="146" stmtstart="12864" stmtend="13140" sqlhandle="0x030007005220010817f24901f9a1000001000000000000000000000000000000000000000000000000000000">
exec sp_datman_get_attr_value @attr_id, @item_key, @value OUTPUT    

  --if attr is required, make sure it has been supplied    </frame>
    <frame procname="WMGDS_Ref.dbo.sp_datman_validate_category" line="12" stmtstart="892" sqlhandle="0x030007008b44f50851f24901f9a1000001000000000000000000000000000000000000000000000000000000">
exec sp_datman_validate_category_role @geo_id, @category_id, 30, @unvalidated_records_only    </frame>
    <frame procname="WMGDS_Ref.dbo.sp_datman_validate_country_segmentation" line="33" stmtstart="1894" stmtend="2110" sqlhandle="0x030007005bcf6449a3f34901f9a1000001000000000000000000000000000000000000000000000000000000">
exec sp_datman_validate_category @geo_id, @category_id, @unvalidated_records_only

    --fetch next record    </frame>
    <frame procname="adhoc" line="1" sqlhandle="0x010005005563e60c10dab88f0b00000000000000000000000000000000000000000000000000000000000000">
[WMGDS_Ref].[dbo].[sp_datman_validate_country_segmentation] '124'    </frame>
    <frame procname="WGDS_Master.dbo.spWGDSFileProcessor_CustomAction_WDIM_Export_Prod_WM" line="95" stmtstart="7378" stmtend="7420" sqlhandle="0x03000500d1db545fa59ca800d8a3000001000000000000000000000000000000000000000000000000000000">
EXEC(@SQL)    </frame>
    <frame procname="WGDS_Master.dbo.spWGDSFileProcessor_FinalizeLoad" line="30" stmtstart="2380" stmtend="2460" sqlhandle="0x03000500822a3374004032001ca8000001000000000000000000000000000000000000000000000000000000">
EXEC @SQL @LoadID    </frame>
    <frame procname="adhoc" line="1" sqlhandle="0x010005009ab97c38301aa2840a00000000000000000000000000000000000000000000000000000000000000">
EXEC spWGDSFileProcessor_FinalizeLoad 9389    </frame>
   </executionStack>
   <inputbuf>
EXEC spWGDSFileProcessor_FinalizeLoad 9389   </inputbuf>
  </process>
  <process id="process386ed48188" taskpriority="0" logused="38816080" waitresource="PAGE: 7:1:1593417 " waittime="822" ownerId="635069358" transactionname="test" lasttranstarted="2017-11-14T04:09:05.813" XDES="0x7109e3a10" lockMode="X" schedulerid="5" kpid="20156" status="suspended" spid="61" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2017-11-14T04:09:05.043" lastbatchcompleted="2017-11-14T04:05:04.870" lastattention="1900-01-01T00:00:00.870" clientapp="Microsoft SQL Server Management Studio - Query" hostname="BDC-WMGXSQLD01" hostpid="68396" loginname="AP\mandalapu.s.1" isolationlevel="read committed (2)" xactid="635069358" currentdb="7" lockTimeout="4294967295" clientoption1="671091040" clientoption2="390200">
   <executionStack>
    <frame procname="adhoc" line="5" stmtstart="86" sqlhandle="0x02000000b91ca9120ff55e660725b95775c8d18d8bff60d30000000000000000000000000000000000000000">
UPDATE 
    D
SET
    D.team_rss_attr_01 = LEFT(S.mkt_prodchar_13,25)
    ,D.team_rss_attr_02 = LEFT(S.mkt_prodchar_14,25)
    ,D.team_rss_attr_03 = LEFT(S.mkt_prodchar_15,25)
    ,D.team_rss_attr_04 = LEFT(S.mkt_prodchar_16,25)
    ,D.team_rss_attr_05 = LEFT(S.mkt_prodchar_17,25)
    ,D.team_rss_attr_08 = LEFT(S.mkt_prodchar_19,25)
FROM 
    tbl_Ref_Prod P
    INNER JOIN tblWGDS_Ref_Prod_Market S ON S.prod_key=P.prod_key
    INNER JOIN tbl_Ref_Attr_Prod_Team D ON D.prod_key=P.prod_key
WHERE 
    P.geo_id='840'
    AND P.prod_type_id=1    </frame>
   </executionStack>
   <inputbuf>
select @@SPID

begin transaction test

UPDATE 
    D
SET
    D.team_rss_attr_01 = LEFT(S.mkt_prodchar_13,25)
    ,D.team_rss_attr_02 = LEFT(S.mkt_prodchar_14,25)
    ,D.team_rss_attr_03 = LEFT(S.mkt_prodchar_15,25)
    ,D.team_rss_attr_04 = LEFT(S.mkt_prodchar_16,25)
    ,D.team_rss_attr_05 = LEFT(S.mkt_prodchar_17,25)
    ,D.team_rss_attr_08 = LEFT(S.mkt_prodchar_19,25)
FROM 
    tbl_Ref_Prod P
    INNER JOIN tblWGDS_Ref_Prod_Market S ON S.prod_key=P.prod_key
    INNER JOIN tbl_Ref_Attr_Prod_Team D ON D.prod_key=P.prod_key
WHERE 
    P.geo_id='840'
    AND P.prod_type_id=1    </inputbuf>
  </process>
 </process-list>
 <resource-list>
  <pagelock fileid="1" pageid="1660625" dbid="7" subresource="FULL" objectname="WMGDS_Ref.dbo.tbl_Ref_Attr_Prod_Team" id="lock16e2f2b280" mode="X" associatedObjectId="72057594191085568">
   <owner-list>
    <owner id="process386ed48188" mode="X" />
   </owner-list>
   <waiter-list>
    <waiter id="process3980de4558" mode="IS" requestType="wait" />
   </waiter-list>
  </pagelock>
  <pagelock fileid="1" pageid="1593417" dbid="7" subresource="FULL" objectname="WMGDS_Ref.dbo.tbl_Ref_Attr_Prod_Team" id="lock18db89be00" mode="U" associatedObjectId="72057594191085568">
   <owner-list>
    <owner id="process3980de4558" mode="IS" />
   </owner-list>
   <waiter-list>
    <waiter id="process386ed48188" mode="X" requestType="convert" />
   </waiter-list>
  </pagelock>
 </resource-list>
</deadlock>

我不熟悉SQL死锁,所以任何帮助都会受到赞赏....

1 个答案:

答案 0 :(得分:1)

导致死锁的两个查询是下面的SELECTprocess id="process3980de4558"):

select @existing = team_it_cube_attr_05 from tbl_Ref_Attr_Prod_Team where prod_id = @rec_key

下面的UPDATE查询(process id="process386ed48188"):

UPDATE D
SET D.team_rss_attr_01 = LEFT(S.mkt_prodchar_13,25)...

<resource-list>部分指出SELECT查询在页面上拥有独占(X)锁,并且在读取数据时尝试获取另一页上的意图共享(IS)锁。 UPDATE查询已拥有IS锁,并尝试在页面上获取X锁以执行更新。

鉴于此表的加入:

...from tbl_Ref_Attr_Prod_Team where prod_id = @rec_key...
...INNER JOIN tbl_Ref_Attr_Prod_Team D ON D.prod_key=P.prod_key...

SELECT查询已拥有独占锁。这可能意味着它是已在先前查询中执行UPDATE的较大事务的一部分。将维护先前查询的锁定以在事务期间保持数据完整性(取决于transaction isolation level)。

UPDATE查询需要阅读表tbl_Ref_Attr_Prod_team。它在读取数据时获取页面和行上的意图共享锁。当UPDATE查询找到匹配的行时,它将尝试将IS锁转换为X锁。 IS locks are not compatible with X locks.因为SELECT查询已经在一个或多个页面上有IS锁定,所以查询会相互死锁。

一个可能的原因是tbl_Ref_Attr_Prod_team.prod_key上缺少索引。如果没有此列的索引,UPDATE查询将扫描表tbl_Ref_Attr_Prod_team中的所有行。

即使prod_key上存在索引,如果表中存在少量行,如果查询扫描整个表而不是查找索引,SQL Server可能会认为性能会更好。发生死锁时记录查询计划将验证这一理论。

在暂存新数据库时,我们会定期遇到小型表死锁。最初,表很小,表扫描会导致各种死锁。稍后,当表格较大时,扫描表格的计算成本超过了查找索引的成本,并且不再发生死锁。在行数永远很小的测试环境中,我们使用FORESEEKWITH INDEX提示来强制索引搜索而不是扫描。我们期待能够通过SQL Server 2016的查询存储功能强制执行查询计划。