5

alt textZakleszczenie w SQL Server 2005! Tworzą się dwie luki w czasie rzeczywistym. CZEMU?

Oto scenariusz:

Mam tabelę o nazwie MarketDataCurrent (MDC), która na żywo aktualizacji cen akcji.

Mam jeden proces o nazwie "LiveFeed", który odczytuje ceny przesyłane strumieniowo z drutu, umieszcza w kolejce wstawki i używa "zbiorczego przesyłania do tabeli tymczasowej, a następnie wstawiania/aktualizowania do tabeli MDC". (BulkUpsert)

Mam inny proces, który odczytuje te dane, oblicza inne dane, a następnie zapisuje wyniki z powrotem do tej samej tabeli, używając podobnego procenta przechowywanego w BulkUpsert.

Po trzecie, istnieje duża liczba użytkowników, którzy uruchamiają C# Gui odpytywanie tabeli MDC i odczytywanie z niej aktualizacji.

Teraz, gdy dane szybko się zmieniają, wszystko działa całkiem sprawnie, ale potem, po godzinach rynkowych, ostatnio zaczęliśmy widzieć coraz więcej wyjątków zakleszczeń wychodzących z bazy danych, obecnie widzimy 10 -20 dziennie. Ważną rzeczą, o której należy pamiętać, jest to, że dzieje się to, gdy wartości NIE ulegają zmianie.

Oto wszystkie istotne informacje:

Tabela 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] 

-

alt text

Mam SQL Profiler śledzenia Bieganie, łapanie zakleszczenia, a oto jak wyglądają wszystkie wykresy.

alt text

Proces 258 nazywa się następujące 'BulkUpsert i przechowywanych Proc wielokrotnie, a 73 jest wywołanie następnej:

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 

a drugi:

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 

Aby wyjaśnić, te tablice Temp są tworzone przez kod C# w tym samym połączeniu i są wypełniane przy użyciu klasy C# SqlBulkCopy.

Dla mnie wygląda na to, że jest to zakleszczenie na PK stołu, więc próbowałem usunąć ten PK i przełączając się na Unikalne ograniczenie, ale to zwiększyło liczbę zakleszczeń 10-krotnie.

Jestem całkowicie zagubiony co do tego, co zrobić w tej sytuacji i jestem otwarty na każdą sugestię.

POMOC !!


W odpowiedzi na wniosek o XDL, tutaj jest:

<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> 
+0

Co się dzieje, jeśli usunąć bezpośrednimi dulka i nolock specyfikatorów? Czy któreś ze źródeł danych są wymienione w widokach zapytań, a nie w tabelach, a jeśli tak, to możesz opublikować źródło widoku? Dzięki. –

+0

Żadne ze źródeł danych nie są widokami. W rzeczywistości dodanie explicite rowlock i nolocks pomogło zmniejszyć liczbę zakleszczeń. – skimania

+1

Lepiej opublikować rzeczywisty zakleszczenie XDL, a nie obraz. Obrazy mogą zwodzić ... http://rusanu.com/2010/05/12/the-puzzle-u--locks-in-deadlock-graphs/ –

Odpowiedz

0

W końcu rozwiązałem ten problem, po prawie dwóch latach denerwujących e-maili z ostrzeżeniem o zakłóceniach.

Rozwiązałem to, używając BLOKADY BLOKADY TABELI na moich konkurencyjnych wkładkach. Próbowałem zmniejszyć blokowanie do poziomu wiersza, ale blokady eskalowały do ​​poziomu tabeli. Ostatecznie zdecydowałem, że stół jest na tyle mały, że chociaż wielu użytkowników czyta go i pisze w każdej sekundzie, że cały zamek jest małym hitem wydajności, który chciałem wziąć na spójność danych.

Dodatkowo, łączenie wstawki/aktualizacji do jednej instrukcji atomowej za pomocą MERGE pozwala mi to zrobić.

Oto rozwiązane kod produkcyjny (to działa!):

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); 
+0

!!! TABLOCKX TABLOCKX TABLOCKX TABLOCKX TABLOCKX TABLOCKX !!! – skimania

+0

WOOOOOOOOOOOOOOOOOOOOOOOOO !!!! – skimania

1

To się dzieje po głównych godzinach pracy, dane nie zmienia się i to właśnie rozpoczęła niedawno. Czy coś ostatnio zmieniło się na serwerze?Podejrzewam, że niektóre nowe prace konserwacyjne bazy danych mogą powodować zakłócenia.

BTW, jeśli wiesz, że rynek jest zamknięty, a dane się nie zmieniają, dlaczego Twój proces nadal działa?

+0

Zarządzam serwerem i wiem wszystko, co na nim działa. Istnieje zadanie kopii zapasowej SQL DTSS, które wykonuje pełną kopię zapasową co noc na tym samym fizycznym dysku twardym, a następnie inny DTSS na innym serwerze SQL, który kopiuje z lokalnego dysku mojego serwera do sieci "Snap Server" – skimania

+0

Uproszczę na temat danych w ogóle nie aktualizować. Większość rynków jest zamknięta, ale FX jest otwarty 24 godziny na dobę, więc musimy przetworzyć ten rynek. – skimania

2

Zakleszczenie wydaje się być bezpośrednim zakleszczeniem w kluczowym zadaniu dostępu. Jednym z trywialnych wyjaśnień jest nakładanie się zaktualizowanych kluczy między dwiema operacjami aktualizacji zbiorczej.

Mniej banalnym wyjaśnieniem jest to, że w SQL Server (i innych serwerach) zablokowane klucze są zaszyfrowane i istnieje (dość znaczące) prawdopodobieństwo kolizji hash. To by wyjaśniało, dlaczego ostatnio widzisz więcej zakleszczeń w porównaniu z wcześniej: po prostu ilość danych zwiększyła się, a tym samym zwiększyło się prawdopodobieństwo kolizji. Jeśli wydaje się to ezoteryczne i nieprawdopodobne, po prostu przeczytaj na stronie %%lockres%% collision probability magic marker: 16,777,215 i artykuł z tego powiązany. Prawdopodobieństwo jest zaskakująco wysokie, ponieważ dla klucza idealny jest 50% prawdopodobieństwo kolizji po zaledwie ~ 16M wstawkach. Dla normalnego, rzeczywistego świata, kluczowe rozkłady mają znaczące prawdopodobieństwo kolizji przy zaledwie kilku tysiącach wstawek. Niestety, nie ma żadnej pracy. Jedynym rozwiązaniem, jeśli rzeczywiście jest to problem, jest zmniejszenie rozmiaru partii (rozmiaru tabel #temp), aby prawdopodobieństwo kolizji było mniejsze. Lub zajmuj się zakleszczeniami i spróbuj ponownie ... co i tak musisz zrobić, ale przynajmniej możesz poradzić sobie z mniejszą liczbą zakleszczeń.

+0

Ten wgląd był bardzo pomocny do tej pory. Nie mogę jeszcze przyjąć żadnych odpowiedzi, dopóki nie wymyślę sposobu na zmniejszenie tych zakleszczeń do akceptowalnego poziomu, ale myślę, że jest to na dobrej drodze. Twój artykuł zawiera linki do tej strony, która jest bardzo interesująca: http://consultingblogs.emc.com/jamesrowlandjones/archive/2009/05/28/the-custom-case-of-tdi-dubious-deadlock-and -the-nie-tak-logiczno-lock.aspx wspomina trzy możliwe rozwiązania tego problemu, które # 1 jest 1. Zmiana kluczem do zastępczy całkowitej oparte klucz Ale to jest dokładnie to, co mam . PK w tej tabeli to unikalna int, nic więcej. – skimania

+0

Nie zgadzam się z Jakubem w tej sprawie. Zmiana klucza na int klucz zastępczy zrobi dokładnie nic, 8 bajtów int będzie nadal hashed do rozmiaru zasobu blokady, który wynosi 6 bajtów. Moim zdaniem. jedynym rozsądnym podejściem jest zmniejszenie wielkości partii. –

+0

prostu zabrakło: wybrać %% lockres %% jako LockHas, count (*) z MarketDataCurrent grupy przez %% lockres %% mającego count (*)> 1 (w środku dnia) i dostałem 0 rzędów! Nasz stół nie jest tak naprawdę taki duży. ~ 32 000 wierszy ogółem (to powinno być bardzo lekkie i mieć tylko dane w czasie rzeczywistym) Tak więc, z unikalnym int id pk, a wartości w zakresie do 250 000 mieszania kluczy hash wydają się prawie niemożliwe jako wyjaśnienie. Uruchomię to zapytanie ponownie zaraz po następnym impasie, aby sprawdzić, czy są jakieś w tym miejscu, ale nie oczekuję ich. – skimania

1

Chciałbym odpowiedzieć na jedno pytanie zadałem w komentarzu, który jest

„Jak można określić wiersze, które są blokujące?”.

W następującym zakleszczeniu XDL, na dwóch "procesowych" węzłach, które są blokowane, znajduje się atrybut waitresource. w tym przypadku:

waitresource="KEY: 6:72057594090487808 (d4005c04b35f)

i

waitresource="KEY: 6:72057594090487808 (b00072ea4ffd)

Używanie %%lockres%% słowo kluczowe aby Remus wskazał,

select %%lockres%%, * from MarketDataCurrent 
    where %%lockres%% in ('(d4005c04b35f)', '(b00072ea4ffd)') 

Dało to dwa wiersze, które są sprzeczne. są to rzeczywiście unikatowe identyfikatory i nie ma kolizji. Wciąż nie wiem, dlaczego dostaję tutaj impas, ale jestem coraz bliżej.

Zaznaczę, że oba identyfikatory powinny pochodzić tylko z programu LiveFeed, ale w aktualizacji jest także klauzula, która ma odfiltrowywać ten wiersz z aktualnej aktualizacji z drugiej strony.

<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> 
Powiązane problemy