Тупик в SQL Server 2005!Два в реальном времени upsserts борются.ЗАЧЕМ? - PullRequest
5 голосов
/ 11 июня 2010

alt text

Вот сценарий:

У меня есть таблица MarketDataCurrent (MDC), которая обновляет цены акций в режиме реального времени.

У меня есть один процесс под названием «LiveFeed», который читает потоковые данные из канала, ставит в очередь вставки и использует «массовую загрузку в временную таблицу, а затем вставку / обновление в таблицу MDC».(BulkUpsert)

У меня есть другой процесс, который затем считывает эти данные, вычисляет другие данные и затем сохраняет результаты обратно в ту же таблицу, используя аналогичный сохраненный процесс BulkUpsert.

В-третьихЕсть множество пользователей, работающих с C # Gui, которые опрашивают таблицу MDC и читают из нее обновления.

Теперь, в течение дня, когда данные быстро меняются, все идет довольно гладко, но затем, после часов работы рынкаНедавно мы начали видеть все большее число исключений Deadlock, выходящих из базы данных, в настоящее время мы видим 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]

-

alt text

У меня работает трассировка Sql Profiler Trace, обнаруживаются тупики, и вот как выглядят все графики.

alt text

Процесс 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

И другой:

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 &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>

Ответы [ 4 ]

2 голосов
/ 12 июня 2010

Кажется, что тупик - это прямые тупики в порядке доступа к ключу. Одним из тривиальных объяснений является совпадение обновленных ключей между двумя операциями массового обновления.

Менее тривиальное объяснение состоит в том, что в SQL Server (и других серверах) заблокированные ключи хэшируются , и существует (довольно существенная) вероятность коллизии хеша. Это объясняет, почему в последнее время вы видите больше взаимоблокировок по сравнению с предыдущим: просто ваш объем данных увеличился и, следовательно, вероятность столкновения увеличилась. Если это кажется эзотерическим и невероятным, просто продолжайте читать на %% lockres %% магический маркер вероятности столкновения: 16,777,215 , и статью, связанную с этим. Вероятность на удивление высока: для идеального распределения ключей вероятность столкновения составляет 50% только после вставки ~ 16M. Для нормального, реального мира распределения ключей вероятность столкновения составляет всего несколько тысяч вставок. К сожалению, вокруг нет работы. Единственное решение, если это действительно проблема, - это уменьшить размер пакетов (размер таблиц #temp), чтобы уменьшить вероятность столкновения. Или справиться с взаимоблокировками и повторить попытку ... что вам придется делать в любом случае, но по крайней мере вы можете справиться с меньшим взаимоблокировками.

1 голос
/ 17 июня 2010

Я хотел бы ответить на один вопрос, который я задал в комментарии, а именно:

«Как определить, какие строки блокируются?».

В следующем взаимоблокировочном XDL на двух блокируемых узлах процесса есть атрибут waitresource. в этом случае:

waitresource="KEY: 6:72057594090487808 (d4005c04b35f)

и

waitresource="KEY: 6:72057594090487808 (b00072ea4ffd)

Используя ключевое слово %%lockres%%, на которое Ремус указал,

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

Это привело к двум противоречивым строкам. это действительно уникальные идентификаторы, и столкновения нет. Я до сих пор не знаю, почему у меня здесь тупик, но я все ближе.

Отмечу, что оба идентификатора должны поступать только из программы LiveFeed, но опять же, в обновлении есть пункт, который должен отфильтровывать эту строку от фактического обновления с другой стороны. 1022 *

<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>
1 голос
/ 11 июня 2010

Это происходит в нерабочее время, данные не меняются, и это началось недавно. Что-нибудь недавно изменилось на сервере? Я подозреваю, что какая-то новая работа по обслуживанию базы данных может помешать.

Кстати, если вы знаете, что рынок закрыт и данные не меняются, почему ваш процесс все еще работает?

0 голосов
/ 29 февраля 2012

Я наконец-то решил эту проблему после почти двух лет надоедливых электронных писем с предупреждениями о тупиках.

Я решил это, используя FULL TABLE LOCKING на своих конкурирующих вставках. Я пытался уменьшить блокировку до уровня строки, но блокировки увеличивались до уровня таблицы. В итоге я решил, что таблица достаточно мала, и хотя многие пользователи читают и пишут в нее каждую секунду, полная блокировка - это небольшой удар по производительности, который я готов был принять за согласованность данных.

Кроме того, объединение вставки / обновления в один атомарный оператор с использованием MERGE позволяет мне сделать это.

Вот разрешенный производственный код (он работает!):

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);
...