SQL Server 2005でデッドロック!二つのリアルタイムのバルクアップサートは戦っています。どうして?
-
26-09-2019 - |
質問
ここではシナリオがあります:
私はライブ更新株価を持つMarketDataCurrent(MDC)と呼ばれるテーブルを持っています。
私は、挿入アップ線からのストリーミング価格、キューを読み取って「LiveFeed」と呼ばれる一つのプロセスを持って、そして使用しました「を挿入/ MDC表への更新後、一時テーブルへの一括アップロードを。」 (BulkUpsert)
Iは、次いで、このデータを読み出して他のデータを計算し、その後、結果は類似BulkUpsertストアドプロシージャを使用して、同じテーブルに戻し保存し、別のプロセスを持っています。
第三に、C#グイポーリングMDCテーブルを実行し、そこから更新を読み出しユーザ多数のがあります。
さて、データが急速に変化している日中、物事はかなりスムーズに実行されますが、その後、市場の時間の後、我々は最近、デッドロック例外の数が増え、今日我々が見る、データベースから出てきて見始めました10- 20日。ここで注意すべきimporant事は値が変更されていない場合にこれらが起こるということです。
ここでは、関連するすべての情報です
表デフます:
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プロファイラトレースを得て、ここですべてのグラフがどのように見えるかだきます。
73は、次の呼び出している間に
プロセス258は、繰り返して、次の「BulkUpsert」ストアドプロシージャと呼ばれる
を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
そしてもう一つます:
ALTER PROCEDURE [dbo].[MarketDataCurrent_LiveFeedUpload]
AS
begin transaction
-- Update existing mdid
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
INSERT INTO MarketDataCurrent with (ROWLOCK) SELECT * FROM #TEMPTABLE2
WHERE MDID NOT IN (SELECT MDID FROM MarketDataCurrent with (NOLOCK))
-- Clean up the temp table
DELETE #TEMPTABLE2
commit
明確にするために、これらの一時テーブルは、同じ接続上のC#コードによって作成されており、C#SqlBulkCopyクラスを使用して移入されます。
私はPK、代わりに一意制約に切り替えることを削除しようとしたので、それは、テーブルのPKでデッドロックているように、私にはそれが見えますが、それはデッドロック10倍の数を増加させました。
私は完全にこの状況をどうすべきことの失われたとちょうど約あらゆる提案に開いていますよ。
HELP !!
<時間>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 < @updateTime
and c.mdid not in (select mdid from MarketData where BloombergTicker is not null and PriceSource like 'Blbg.%')
and c.value <> 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>
解決 4
私は迷惑なデッドロック警告メールのほぼ2年後に、この問題を最終的に解決した。
私は私の競合インサート上でFULL TABLEロックを使用して、それを解決しました。 Iは、行レベルのロックを低減しようとしたが、ロックは、テーブルレベルにエスカレートしました。最後に、私はテーブルを決めたユーザーの多くがフルロックが小さなパフォーマンスであったことを、毎秒を読んで、それを書いているにもかかわらず、私は、データの一貫性のために取って喜んでいたヒットという。
小さな十分でしたまた、MERGEを使用して1つの原子の文に挿入/更新を組み合わせること私はこれを行うことができます。
ここで解決本番コードは(それが動作!)です。
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);
他のヒント
デッドロックは、キーアクセス順序にまっすぐ進むデッドロックのようです。一つの些細な説明は、二つのバルク更新操作の間に更新されたキーの重なりです。
しかしA以下些細な説明は、そのSQL Serverの(および他のサーバーすぎ)でロックされたキーは、のハッシュされたのであり、(かなり大きな)ハッシュ衝突の可能性があります。単にデータのあなたの量が増加しているので、衝突確率が増加している:あなたは最近前と比較してより多くのデッドロックを参照してください理由を説明するだろう。これは難解とそうにないと思われる場合は、単にで読みlockres %%衝突確率のマジックマーカー%%:16777215 に、そしてそこからリンク先の記事。確率はあなただけ〜16Mを挿入した後、50%の衝突確率を持っているの完璧なのキーの配布のために、驚くほど高いです。通常、現実の世界では、主要ディストリビューションでは、あなたはわずか数千のインサートで有意衝突確率を持っています。残念ながら、仕事の周りにはありません。これは本当に問題であれば、あなたの唯一の解決策は、衝突確率が減少するようにバッチ(#TEMPテーブルの大きさ)のサイズを小さくすることです。とにかく行う必要があるでしょうデッドロック再試行...との契約が、少なくとも、あなたが扱うことができるか、の少ないのデッドロックます。
それがメインの業務時間後に起こって、データが変更されていない、それはちょうど最近始めました。何でも最近は、サーバー上で変更しましたか?私はいくつかの新しいデータベース保守ジョブが妨害される可能性があります疑いがあるでしょう。
あなたは市場が閉じている知っていると、データが変更されていない場合ところで、なぜまだあなたのプロセスで実行している?
私は、私がコメントで尋ねた一つの質問にお答えしたいと思います。
「どのようにしてロックしている行を識別しますか?」。
次のデッドロックXDLでは、ロックされた2つの「プロセス」ノードで、waitresource
属性があります。この場合:
waitresource="KEY: 6:72057594090487808 (d4005c04b35f)
と
waitresource="KEY: 6:72057594090487808 (b00072ea4ffd)
レムスが指摘%%lockres%%
キーワードを使用する、
select %%lockres%%, * from MarketDataCurrent
where %%lockres%% in ('(d4005c04b35f)', '(b00072ea4ffd)')
これは、競合している2つの行を得ました。彼らは確かに固有の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 < @updateTime
and c.mdid not in (select mdid from MarketData where BloombergTicker is not null and PriceSource like 'Blbg.%')
and c.value <> 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>