Сегодня у нас был очень интересный случай, который меня очень озадачивает. В двух словах мы сделали несколько рефакторингов, очистив триггеры от повторяющегося кода, извлекая его в единую и многоразовую хранимую процедуру. Мы думали, что этот рефакторинг не будет иметь побочных эффектов, но мы были неправы. После выпуска мы столкнулись с множеством тупиков и ухудшением производительности без каких-либо очевидных причин. После проверки системных таблиц, чтобы увидеть, какая БД взята, мы выяснили, что рефакторинг выше был задействован, и мы закончили с откатом от обновления.Тупиковые замки после извлечения части триггера в хранимую процедуру
Мы не воспроизводили проблему в тестовых средах, чтобы исследовать лекарство, поэтому некоторые проблемы возникают, чтобы проблема была видимой.
Ниже приведены сведения о том, что изменилось. Мы обновили множество триггеров, но все они очень похожи, я покажу вам одно. Этого должно быть достаточно, поскольку я нашел тупиковый график, который показывает, что существует тупик, когда два процесса выполняли единственный триггер (показать ниже) и зашли в тупик.
Позвольте мне начать с решения, которое работало до этого (мы вернулись и выглядели почти идентично решению взаимоблокировки, показанному ниже).
CREATE TRIGGER [dbo].[TR__xyz__update_sync_publishers]
ON [dbo].[xyz]
AFTER INSERT, DELETE, UPDATE
AS
BEGIN
SET NOCOUNT ON;
if(TRIGGER_NESTLEVEL() = 1)
BEGIN
create table #AffectedIDs (advisor_id int primary key)
insert into #AffectedIDs
select distinct t.id
from
(select id
from inserted
inner join xyz a ON a.id = id
where [text] <> ''
union
select id
from inserted
inner join xyz a ON a.id = id
where [text] <> '') t
declare @date datetime = getutcdate()
declare @RegisteredObjectTypeID int = 2
declare @SyncPublisherSourceID int = 1
update pub
set pub.master_update_date = @date
from #AffectedIDs affected
inner join sync_publishers pub on
pub.sync_registered_object_type_id = @RegisteredObjectTypeID
and pub.sync_publisher_source_id = @SyncPublisherSourceID
and pub.sync_object_id = affected.advisor_id
insert into sync_publishers (sync_object_id, sync_registered_object_type_id, sync_publisher_source_id , master_update_date)
select
affected.advisor_id,
@RegisteredObjectTypeID,
@SyncPublisherSourceID,
@date
from #AffectedIDs affected
left join sync_publishers pub on
pub.sync_registered_object_type_id = @RegisteredObjectTypeID
and pub.sync_publisher_source_id = @SyncPublisherSourceID
and pub.sync_object_id = affected.advisor_id
where
pub.sync_object_id is null
drop table #AffectedIDs
END
END
Вот новый триггер, который блокирует.
CREATE TRIGGER [dbo].[TR__xyz__update_sync_publishers]
ON [dbo].[xyz]
AFTER INSERT,DELETE,UPDATE
AS
BEGIN
SET NOCOUNT ON;
declare @ids dtInt
insert into @ids
select distinct t.id
from
(
select id from inserted
INNER JOIN xyz a ON a.id = id
WHERE [text] <> ''
union
select id from inserted
INNER JOIN xyz a ON a.id = id
WHERE [text] <> ''
) t
exec SyncTracker_PublishEvent 2, @ids
END
Вот определение экстрагированной SP:
CREATE PROCEDURE [dbo].[SyncTracker_PublishEvent]
@objectTypeId int,
@ids dtInt readonly
AS
BEGIN
SET NOCOUNT ON;
if(TRIGGER_NESTLEVEL() > 1) RETURN;
declare @pubSourceId int = 1
declare @date datetime = getutcdate()
update pub
set pub.master_update_date = @date
from @ids affected
inner join sync_publishers pub
on pub.sync_registered_object_type_id = @objectTypeId
and pub.sync_publisher_source_id = @pubSourceId
and pub.sync_object_id = affected.value
insert into sync_publishers (sync_object_id, sync_registered_object_type_id, sync_publisher_source_id , master_update_date)
select affected.value, @objectTypeId, @pubSourceId, @date
from @ids affected
left join sync_publishers pub
on pub.sync_registered_object_type_id = @objectTypeId
and pub.sync_publisher_source_id = @pubSourceId
and pub.sync_object_id = affected.value
where
pub.sync_object_id is null
END
GO
Определение dtInt.
CREATE TYPE [dbo].[dtInt] AS TABLE
(
[value] [int] NOT NULL,
PRIMARY KEY CLUSTERED
(
[value] ASC
)
)
И, наконец, график взаимоблокировки.
<deadlock>
<victim-list>
<victimProcess id="processe1892fe8c8" />
</victim-list>
<process-list>
<process id="processe1892fe8c8" taskpriority="0" logused="3824" waitresource="KEY: 5:72057602924150784 (4776e78e2961)" waittime="5686" ownerId="2583257965" transactionname="user_transaction" lasttranstarted="2016-10-03T08:30:42.500" XDES="0xe192b24408" lockMode="U" schedulerid="6" kpid="41296" status="suspended" spid="141" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2016-10-03T08:30:42.503" lastbatchcompleted="2016-10-03T08:30:42.493" lastattention="2016-10-03T08:29:01.693" clientapp="..." hostname="..." hostpid="22572" loginname="kbuser" isolationlevel="read committed (2)" xactid="2583257965" currentdb="5" lockTimeout="4294967295" clientoption1="673316896" clientoption2="128056">
<executionStack>
<frame procname="63c1b4d8-1c55-4429-b057-81fb6da8f780.dbo.SyncTracker_PublishEvent" line="21" stmtstart="1178" stmtend="1680" sqlhandle="0x030005007bf23c4b5012b40092a6000001000000000000000000000000000000000000000000000000000000">
update pub
set pub.master_update_date = @date
from @ids affected
inner join sync_publishers pub
on pub.sync_registered_object_type_id = @objectTypeId
and pub.sync_publisher_source_id = @pubSourceId
and pub.sync_object_id = affected.valu </frame>
<frame procname="63c1b4d8-1c55-4429-b057-81fb6da8f780.dbo.TR__xyz__update_sync_publishers" line="28" stmtstart="1300" stmtend="1372" sqlhandle="0x03000500f711233ddee4c60090a6000000000000000000000000000000000000000000000000000000000000">
exec SyncTracker_PublishEvent 2, @id </frame>
<frame procname="unknown" line="1" stmtstart="1054" stmtend="3032" sqlhandle="0x02000000912653235c5ef3529289f19ae4445e62ee1ccbc00000000000000000000000000000000000000000">
unknown </frame>
<frame procname="unknown" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
</process>
<process id="processdfa401b848" taskpriority="0" logused="9384" waitresource="KEY: 5:72057602924150784 (1501093f83b4)" waittime="5814" ownerId="2582414029" transactionname="user_transaction" lasttranstarted="2016-10-03T08:30:09.933" XDES="0x104486ac408" lockMode="U" schedulerid="1" kpid="19548" status="suspended" spid="213" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2016-10-03T08:30:53.047" lastbatchcompleted="2016-10-03T08:30:53.047" lastattention="1900-01-01T00:00:00.047" clientapp="..." hostname="..." hostpid="6196" loginname="kbuser" isolationlevel="read committed (2)" xactid="2582414029" currentdb="5" lockTimeout="4294967295" clientoption1="673316896" clientoption2="128056">
<executionStack>
<frame procname="63c1b4d8-1c55-4429-b057-81fb6da8f780.dbo.SyncTracker_PublishEvent" line="21" stmtstart="1178" stmtend="1680" sqlhandle="0x030005007bf23c4b5012b40092a6000001000000000000000000000000000000000000000000000000000000">
update pub
set pub.master_update_date = @date
from @ids affected
inner join sync_publishers pub
on pub.sync_registered_object_type_id = @objectTypeId
and pub.sync_publisher_source_id = @pubSourceId
and pub.sync_object_id = affected.valu </frame>
<frame procname="63c1b4d8-1c55-4429-b057-81fb6da8f780.dbo.TR__xyz__update_sync_publishers" line="28" stmtstart="1300" stmtend="1372" sqlhandle="0x03000500f711233ddee4c60090a6000000000000000000000000000000000000000000000000000000000000">
exec SyncTracker_PublishEvent 2, @id </frame>
<frame procname="unknown" line="1" stmtstart="1120" stmtend="3132" sqlhandle="0x020000007414d821ed68a2ab4462b4eca6b2fdb4ba28cc350000000000000000000000000000000000000000">
unknown </frame>
<frame procname="unknown" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
</process>
</process-list>
<resource-list>
<keylock hobtid="72057602924150784" dbid="5" objectname="63c1b4d8-1c55-4429-b057-81fb6da8f780.dbo.sync_publishers" indexname="IX__sync_publishers__registered_object_type_id__sync_object_id" id="lock10887a96b00" mode="X" associatedObjectId="72057602924150784">
<owner-list>
<owner id="processdfa401b848" mode="X" />
</owner-list>
<waiter-list>
<waiter id="processe1892fe8c8" mode="U" requestType="wait" />
</waiter-list>
</keylock>
<keylock hobtid="72057602924150784" dbid="5" objectname="63c1b4d8-1c55-4429-b057-81fb6da8f780.dbo.sync_publishers" indexname="IX__sync_publishers__registered_object_type_id__sync_object_id" id="lockdb7d7b8200" mode="X" associatedObjectId="72057602924150784">
<owner-list>
<owner id="processe1892fe8c8" mode="X" />
</owner-list>
<waiter-list>
<waiter id="processdfa401b848" mode="U" requestType="wait" />
</waiter-list>
</keylock>
</resource-list>
</deadlock>
Определение sync_publishers доступных здесь: http://pastebin.com/LviwwCDi.
Если у вас есть мысли о возможных причинах - пожалуйста, поделитесь им - мы будем очень признательны!
UPDATE 1. Фактические планы выполнения UPDATE/INSERT в sync_publishers
Фактические планы выполнения выглядит практически идентичны.
новый exec план (это иногда тупиковые блоки).
Старый план exec (это не так).
UPDATE 2. Пробовал некоторые советы
Я попытался несколько советов сегодня:
Избавился «ключевых поисков» в планах запросов из-за отсутствия
sync_publisher_source_id
внутри не- кластеризованный индекс по столбцу удаления - это не было действительно обязательным в нашей реализации.Перепечатка
UPDATE + INSERT
как отдельнаяMERGE
инструкция.MERGE sync_publishers2 t USING @ids s ON s.[value] = t.sync_object_id and t.sync_registered_object_type_id = @objectTypeId WHEN MATCHED THEN UPDATE SET master_update_date = @date WHEN NOT MATCHED THEN INSERT (sync_object_id, sync_registered_object_type_id, master_update_date) VALUES (s.[value], @objectTypeId, @date);
начали получать тупиков о MERGE
отчетности. Новый график взаимоблокировки можно посмотреть здесь: http://pastebin.com/QNJk7tea.
UPDATE 3. Попытка MERGE намеки
Я пытался сделать MERGE
с xlock
и holdlock
намеки - не повезло, хотя - опять попал в тупик на MERGE.
MERGE sync_publishers2 with(xlock, holdlock) t
Попробуйте проверить TRIGGER_NESTLEVEL() в теле триггера. В вашей версии proc этот код может выполняться дважды: 'insert into @ids выберите отличный t.id from()', который может сильно повлиять на время блокировки замков. – Serg
Проверьте фактические планы выполнения инструкций «UPDATE pub» и «INSERT INTO sync_publishers» из триггера против SP. Вы получаете разные планы выполнения из-за оценки мощности? –
@ RazvanSocol, спасибо за предложение! Я обновляю сообщение с фактическими планами выполнения для обоих случаев - выглядит почти идентично ... –