Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sql-server-2005/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server 2005 SQLServer2005中的死锁!两个实时批量升级站正在战斗。为什么?_Sql Server 2005_Deadlock_Bulk_Primary Key_Database Deadlocks - Fatal编程技术网

Sql server 2005 SQLServer2005中的死锁!两个实时批量升级站正在战斗。为什么?

Sql server 2005 SQLServer2005中的死锁!两个实时批量升级站正在战斗。为什么?,sql-server-2005,deadlock,bulk,primary-key,database-deadlocks,Sql Server 2005,Deadlock,Bulk,Primary Key,Database Deadlocks,以下是场景: 我有一个名为MarketDataCurrentMDC的表,它实时更新股票价格 我有一个名为“LiveFeed”的进程,它从线路上读取价格流,将插入内容排队,并使用“批量上传到临时表,然后插入/更新到MDC表” 我有另一个进程,它读取这些数据,计算其他数据,然后使用类似的BulkUpsert存储过程将结果保存回同一个表中 第三,大量用户运行C Gui轮询MDC表并从中读取更新 现在,在数据快速变化的日子里,事情运行得非常顺利,但在市场时段之后,我们最近开始看到数据库中出现越来越多的死

以下是场景:

我有一个名为MarketDataCurrentMDC的表,它实时更新股票价格

我有一个名为“LiveFeed”的进程,它从线路上读取价格流,将插入内容排队,并使用“批量上传到临时表,然后插入/更新到MDC表”

我有另一个进程,它读取这些数据,计算其他数据,然后使用类似的BulkUpsert存储过程将结果保存回同一个表中

第三,大量用户运行C Gui轮询MDC表并从中读取更新

现在,在数据快速变化的日子里,事情运行得非常顺利,但在市场时段之后,我们最近开始看到数据库中出现越来越多的死锁异常,现在我们每天看到10-20个。这里需要注意的重要一点是,当值不变时,这些情况就会发生

以下是所有相关信息:

表Def:

CREATE TABLE [dbo].[MarketDataCurrent](
 [MDID] [int] NOT NULL,
 [LastUpdate] [datetime] NOT NULL,
 [Value] [float] NOT NULL,
 [Source] [varchar](20) NULL, 
CONSTRAINT [PK_MarketDataCurrent] PRIMARY KEY CLUSTERED 
(
 [MDID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, 
ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
-

我已经运行了一个Sql事件探查器跟踪,捕捉到了死锁,下面是所有图表的样子

进程258被反复调用为以下“BulkUpsert”存储进程,而进程73调用下一个进程:

ALTER proc [dbo].[MarketDataCurrent_BulkUpload]
 @updateTime datetime,
 @source varchar(10)
as

begin transaction

update c with (rowlock) set LastUpdate = getdate(), Value = t.Value, Source = @source 
from MarketDataCurrent c INNER JOIN #MDTUP t ON c.MDID = t.mdid
where c.lastUpdate < @updateTime
and   c.mdid not in (select mdid from MarketData where LiveFeedTicker is not null     and     PriceSource like 'LiveFeed.%')
and   c.value <> t.value

insert  into MarketDataCurrent
with (rowlock)
select  MDID, getdate(), Value, @source from #MDTUP 
where mdid not in (select mdid from MarketDataCurrent with (nolock))
and  mdid not in (select mdid from MarketData where LiveFeedTicker is not null     and PriceSource like 'LiveFeed.%')

commit
为了澄清,这些临时表是由同一连接上的C代码创建的,并使用C SqlBulkCopy类填充

对我来说,它看起来像是表的PK上的死锁,所以我尝试移除该PK并切换到一个唯一的约束,但这将死锁的数量增加了10倍

我完全不知道该怎么办,我愿意接受任何建议

救命

作为对XDL请求的响应,如下所示:

<deadlock-list>
 <deadlock victim="processc19978">
  <process-list>
   <process id="processaf0b68" taskpriority="0" logused="0" waitresource="KEY: 6:72057594090487808 (d900ed5a6cc6)" waittime="718" ownerId="1102128174" transactionname="user_transaction" lasttranstarted="2010-06-11T16:30:44.750" XDES="0xffffffff817f9a40" lockMode="U" schedulerid="3" kpid="8228" status="suspended" spid="73" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2010-06-11T16:30:44.750" lastbatchcompleted="2010-06-11T16:30:44.750" clientapp=".Net SqlClient Data Provider" hostname="RISKAPPS_VM" hostpid="3836" loginname="RiskOpt" isolationlevel="read committed (2)" xactid="1102128174" currentdb="6" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="MKP_RISKDB.dbo.MarketDataCurrent_BulkUpload" line="28" stmtstart="1062" stmtend="1720" sqlhandle="0x03000600a28e5e4ef4fd8e00849d00000100000000000000">
UPDATE c WITH (ROWLOCK) SET LastUpdate = getdate(), Value = t.Value, Source = @source 
FROM MarketDataCurrent c INNER JOIN #MDTUP t ON c.MDID = t.mdid
WHERE c.lastUpdate &lt; @updateTime
and   c.mdid not in (select mdid from MarketData where BloombergTicker is not null and PriceSource like &apos;Blbg.%&apos;)
and   c.value &lt;&gt; t.value     </frame>
     <frame procname="adhoc" line="1" stmtstart="88" sqlhandle="0x01000600c1653d0598706ca7000000000000000000000000">
exec MarketDataCurrent_BulkUpload @clearBefore, @source     </frame>
     <frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">
unknown     </frame>
    </executionStack>
    <inputbuf>
(@clearBefore datetime,@source nvarchar(10))exec MarketDataCurrent_BulkUpload @clearBefore, @source    </inputbuf>
   </process>
   <process id="processc19978" taskpriority="0" logused="0" waitresource="KEY: 6:72057594090487808 (74008e31572b)" waittime="718" ownerId="1102128228" transactionname="user_transaction" lasttranstarted="2010-06-11T16:30:44.780" XDES="0x380be9d8" lockMode="U" schedulerid="5" kpid="8464" status="suspended" spid="248" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2010-06-11T16:30:44.780" lastbatchcompleted="2010-06-11T16:30:44.780" clientapp=".Net SqlClient Data Provider" hostname="RISKBBG_VM" hostpid="4480" loginname="RiskOpt" isolationlevel="read committed (2)" xactid="1102128228" currentdb="6" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="MKP_RISKDB.dbo.MarketDataCurrentBlbgRtUpload" line="14" stmtstart="840" stmtend="1220" sqlhandle="0x03000600005f9d24c8878f00849d00000100000000000000">
UPDATE c WITH (ROWLOCK) SET LastUpdate = t.LastUpdate, Value = t.Value, Source = t.Source 
        FROM MarketDataCurrent c INNER JOIN #TEMPTABLE2 t ON c.MDID = t.mdid;

        -- Insert new MDID     </frame>
     <frame procname="adhoc" line="1" sqlhandle="0x010006004a58132228bf8d73000000000000000000000000">
MarketDataCurrentBlbgRtUpload     </frame>
    </executionStack>
    <inputbuf>
MarketDataCurrentBlbgRtUpload    </inputbuf>
   </process>
  </process-list>
  <resource-list>
   <keylock hobtid="72057594090487808" dbid="6" objectname="MKP_RISKDB.dbo.MarketDataCurrent" indexname="PK_MarketDataCurrent" id="lock5ba77b00" mode="U" associatedObjectId="72057594090487808">
    <owner-list>
     <owner id="processc19978" mode="U"/>
    </owner-list>
    <waiter-list>
     <waiter id="processaf0b68" mode="U" requestType="wait"/>
    </waiter-list>
   </keylock>
   <keylock hobtid="72057594090487808" dbid="6" objectname="MKP_RISKDB.dbo.MarketDataCurrent" indexname="PK_MarketDataCurrent" id="lock65dca340" mode="U" associatedObjectId="72057594090487808">
    <owner-list>
     <owner id="processaf0b68" mode="U"/>
    </owner-list>
    <waiter-list>
     <waiter id="processc19978" mode="U" requestType="wait"/>
    </waiter-list>
   </keylock>
  </resource-list>
 </deadlock>
</deadlock-list>

它发生在主营业时间之后,数据没有变化,而且最近才开始。最近服务器上有什么变化吗?我怀疑一些新的数据库维护工作可能会造成干扰


顺便说一句,如果你知道市场已经关闭,数据没有变化,为什么你的流程还在运行?

这个死锁似乎是密钥访问顺序上的直接死锁。一个简单的解释是两个批量更新操作之间的更新键重叠


一个不那么简单的解释是,在SQL Server和其他服务器中,锁定的密钥也是散列的,并且存在相当大的散列冲突概率。这就解释了为什么最近出现的死锁比以前更多:只是数据量增加了,因此冲突概率增加了。如果这似乎是深奥和不可能的,请继续阅读,以及与之相关的文章。概率惊人地高,对于一个完美的密钥分配,仅插入~16M后,您就有50%的碰撞概率。对于正常的、真实的密钥分布,只有几千次插入时才有显著的碰撞概率。不幸的是,没有解决办法。如果这确实是问题所在,那么您唯一的解决方案就是减少批处理的大小和临时表的大小,从而降低冲突概率。或者处理死锁并重试。。。无论如何,你都必须这样做,但至少你可以处理更少的死锁。

我想回答我在评论中提出的一个问题,那就是

如何识别正在锁定的行

在下面的死锁XDL中,在锁定的两个进程节点上,有一个waitresource属性。在这种情况下:

waitresource=KEY:6:72057594090487808 d4005c04b35f

waitresource=KEY:6:72057594090487808 b00072ea4ffd

使用Remus指向的%%lockres%%关键字

select %%lockres%%, * from MarketDataCurrent 
   where %%lockres%% in ('(d4005c04b35f)', '(b00072ea4ffd)')
这产生了冲突的两行。它们确实是唯一的ID,并且没有冲突。我仍然不知道为什么我会在这里陷入僵局,但我越来越近了

我要注意的是,这两个id都应该只来自LiveFeed程序,但是更新中有一个子句应该从另一端的实际更新中过滤掉这一行

<deadlock-list>
 <deadlock victim="processffffffff8f5872e8">
  <process-list>
   <process id="process8dcb68" taskpriority="0" logused="1256" waitresource="KEY: 6:72057594090487808 (d4005c04b35f)" waittime="1906" ownerId="1349627324" transactionname="user_transaction" lasttranstarted="2010-06-16T16:50:04.727" XDES="0x424e6258" lockMode="U" schedulerid="2" kpid="1004" status="suspended" spid="683" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2010-06-16T16:50:04.727" lastbatchcompleted="2010-06-16T16:50:04.727" clientapp=".Net SqlClient Data Provider" hostname="RISKAPPS_VM" hostpid="2600" loginname="RiskOpt" isolationlevel="read committed (2)" xactid="1349627324" currentdb="6" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="MKP_RISKDB.dbo.MarketDataCurrent_BulkUpload" line="28" stmtstart="1062" stmtend="1720" sqlhandle="0x03000600a28e5e4ef4fd8e00849d00000100000000000000">
        UPDATE c WITH (ROWLOCK) SET LastUpdate = getdate(), Value = t.Value, Source = @source 
        FROM MarketDataCurrent c INNER JOIN #MDTUP t ON c.MDID = t.mdid
        WHERE c.lastUpdate &lt; @updateTime
        and   c.mdid not in (select mdid from MarketData where BloombergTicker is not null and PriceSource like &apos;Blbg.%&apos;)
        and   c.value &lt;&gt; t.value     </frame>
             <frame procname="adhoc" line="1" stmtstart="88" sqlhandle="0x01000600c1653d0598706ca7000000000000000000000000">
        exec MarketDataCurrent_BulkUpload @clearBefore, @source     </frame>
     <frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">unknown</frame>
    </executionStack>
    <inputbuf>(@clearBefore datetime,@source nvarchar(10))exec MarketDataCurrent_BulkUpload @clearBefore, @source</inputbuf>
   </process>
   <process id="processffffffff8f5872e8" taskpriority="0" logused="0" waitresource="KEY: 6:72057594090487808 (b00072ea4ffd)" waittime="1921" ownerId="1349627388" transactionname="user_transaction" lasttranstarted="2010-06-16T16:50:04.757" XDES="0x289ea040" lockMode="U" schedulerid="5" kpid="11192" status="suspended" spid="382" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2010-06-16T16:50:04.757" lastbatchcompleted="2010-06-16T16:50:04.757" clientapp=".Net SqlClient Data Provider" hostname="RISKBBG_VM" hostpid="2452" loginname="RiskOpt" isolationlevel="read committed (2)" xactid="1349627388" currentdb="6" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="MKP_RISKDB.dbo.MarketDataCurrentBlbgRtUpload" line="14" stmtstart="840" stmtend="1220" sqlhandle="0x03000600005f9d24c8878f00849d00000100000000000000">
        UPDATE c WITH (ROWLOCK) SET LastUpdate = t.LastUpdate, Value = t.Value, Source = t.Source 
        FROM MarketDataCurrent c INNER JOIN #TEMPTABLE2 t ON c.MDID = t.mdid;
    </frame>
     <frame procname="adhoc" line="1" sqlhandle="0x010006004a58132228bf8d73000000000000000000000000">
        MarketDataCurrentBlbgRtUpload     </frame>
    </executionStack>
    <inputbuf>
        MarketDataCurrentBlbgRtUpload    </inputbuf>
   </process>
  </process-list>
  <resource-list>
   <keylock hobtid="72057594090487808" dbid="6" objectname="MKP_RISKDB.dbo.MarketDataCurrent" indexname="PK_MarketDataCurrent" id="lock409d32c0" mode="U" associatedObjectId="72057594090487808">
    <owner-list>
     <owner id="processffffffff8f5872e8" mode="U"/>
    </owner-list>
    <waiter-list>
     <waiter id="process8dcb68" mode="U" requestType="wait"/>
    </waiter-list>
   </keylock>
   <keylock hobtid="72057594090487808" dbid="6" objectname="MKP_RISKDB.dbo.MarketDataCurrent" indexname="PK_MarketDataCurrent" id="lock706647c0" mode="U" associatedObjectId="72057594090487808">
    <owner-list>
     <owner id="process8dcb68" mode="U"/>
    </owner-list>
    <waiter-list>
     <waiter id="processffffffff8f5872e8" mode="U" requestType="wait"/>
    </waiter-list>
   </keylock>
  </resource-list>
 </deadlock>
</deadlock-list>

经过近两年恼人的死锁警告邮件,我终于解决了这个问题

我通过在我的竞争插件上使用全表锁定解决了这个问题。我曾尝试将锁定减少到行级别,但锁定正在升级到表级别。最后,我认为这个表足够小,即使很多用户每秒都在读写它,但为了数据一致性,一个完全锁定是一个很小的性能损失

此外,使用MERGE将insert/update组合到一个原子语句中,使我能够做到这一点

下面是解析的生产代码,它可以工作!:

declare @date datetime;
set @date = getdate();

merge marketdatacurrent with (tablockx) as mdc

using #MDTUP as upload
    on mdc.MDID = upload.MDID

when matched then
    update
    set mdc.lastupdate = @date,
        mdc.value = upload.value,
        mdc.source = @source

when not matched then
    insert ( mdid, lastupdate, value, source )
    values ( upload.mdid, @date, upload.value, @source);

如果删除显式的rowlock和nolock说明符,会发生什么?您的查询视图中是否有提及的数据源而不是表中的数据源,如果有,您是否可以请求
se发布视图源代码?谢谢。所有数据源都不是视图。实际上,添加explict rowlock和NOLOCK有助于减少死锁的数量。最好发布实际的死锁XDL,而不是映像。图像可能是骗人的。。。这确实是一篇有趣的文章,现在我有一些额外的东西要注意,但正如您从这个XDL中看到的,它们似乎都是U锁。我会再看一些,看看我是否能找到一个不是的。+1只是因为这个问题记录得有多好,如果没有其他问题,我会管理服务器并了解服务器上运行的一切。有一个SQL DTSS备份作业,它每天晚上对同一个物理硬盘进行完全备份,然后在另一台SQL服务器上执行另一个DTS,它从我服务器的本地驱动器复制到网络“快照服务器”。我对数据根本不更新的说法过于简单了。大多数市场是封闭的,但外汇实际上是全天候开放的,所以我们需要处理这个市场。到目前为止,这一观点非常有用。我还不能接受任何答案,直到我想出一种方法将这些僵局减少到一个可接受的水平,但我认为这是在正确的轨道上。你的文章链接到这一个,这是非常有趣的:他提到了这个问题的三种可能的解决方案,其中1是1。将密钥更改为基于代理整数的密钥,但这正是我所拥有的。这个表中的PK是一个唯一的int,没有别的,我实际上不同意James的观点。将密钥更改为int代理密钥将完全不起任何作用,8个字节的int仍然会散列到锁资源大小,即6个字节。依我拙见唯一合理的方法是减少批处理大小。只需运行:选择%%lockres%%作为LockHas,通过%%lockres%%从MarketDataCurrent组计数,在中午计数>1,得到0行!我们的桌子其实没那么大~总共32000行它应该是非常轻量级的,并且只有实时数据,因此,使用一个唯一的int-id pk,以及多达250000个哈希键冲突的值,作为一种解释,似乎几乎不可能。我将在下一次死锁后再次运行此查询,以查看在该点是否存在任何死锁,但我不希望出现这些死锁。此外,FWIW,一个进程的批大小约为200-300行,另一个进程的批大小约为5000行。另外一个问题,您知道如何从上面的XDL解析死锁行的实际ID吗?
declare @date datetime;
set @date = getdate();

merge marketdatacurrent with (tablockx) as mdc

using #MDTUP as upload
    on mdc.MDID = upload.MDID

when matched then
    update
    set mdc.lastupdate = @date,
        mdc.value = upload.value,
        mdc.source = @source

when not matched then
    insert ( mdid, lastupdate, value, source )
    values ( upload.mdid, @date, upload.value, @source);