2016-10-03 7 views
2

오늘 우리는 나를 매우 신비하게 만드는 매우 흥미로운 사례가있었습니다. 한 마디로 우리는 리팩터링을 반복하여 반복 코드에서 트리거를 정리하여 단일 및 재사용 가능한 저장 프로 시저로 추출했습니다. 이 리팩터링은 부작용이 없을 것이라고 생각했지만 잘못되었습니다. 릴리스 이후에 우리는 분명한 이유없이 많은 교착 상태와 성능 저하를 경험했습니다. 시스템 테이블을 검사하여 어떤 DB를 가져 왔는지 확인한 결과 위의 리팩토링과 관련되어 있다는 것을 알았고 업데이트를 롤백하는 것으로 끝 맺었습니다.트리거의 일부를 저장 프로 시저로 추출한 후 교착 상태가 발생했습니다.

아직 테스트를 검증하기 위해 테스트 환경에서 문제를 재현하지 않았으므로 문제가 표시 될 수 있도록 약간의 까다로운 조건이 발생합니다.

다음은 변경 사항에 대한 세부 정보입니다. 우리는 많은 트리거를 업데이트했지만 모두 비슷합니다. 내가 보여줄 것입니다. 두 개의 프로세스가 단일 트리거 (아래 보여주기)를 실행하고 교착 상태가 발생하는 교착 상태가 있음을 보여주는 교착 상태 그래프를 발견 했으므로 충분해야합니다.

이전에 작동했던 솔루션부터 시작해 보겠습니다. (다시 살펴보면 아래 표시된 교착 상태 해결 방법과 거의 동일합니다.)

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 

여기 교착 상태가되는 새로운 트리거가 있습니다. dtInt의

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 

정의 : 여기

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

deadlock graph

여기에 해당 sync_publishers의 정의

: http://pastebin.com/LviwwCDi.

실현 가능한 원인에 대한 의견이 있으시면 - 공유 하시길 바랍니다 - 매우 감사하겠습니다!

UPDATE UPDATE 1. 실제 실행 계획/sync_publishers

실제 실행 계획에 삽입은 동일 꽤 많이 보인다.

새 실행 계획 (가끔 교착 상태). new

이전 실행 계획 (해당하지 않음).

  1. 가 아닌 내 sync_publisher_source_id 누락으로 인해 쿼리 계획에서 "키 조회"를 제거 당함 : old

    UPDATE 2. 시도 일부는

    이 좀 오늘 조언 시도 않았다 조언 클러스터 된 인덱스를 완전히 삭제 컬럼 - 우리의 구현에 정말 필수 아니었다.

  2. 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는

나는 xlockholdlock 힌트 MERGE을 시도한 힌트 다시 MERGE에 교착 상태를 얻었다.

MERGE sync_publishers2 with(xlock, holdlock) t 
+0

트리거 본문에 TRIGGER_NESTLEVEL()를 확인하십시오. proc 버전에서는이 코드가 두 번 실행될 수 있습니다 :'insert into @ids select t.id from()'이것은 잠금이 유지되는 기간에 심각한 영향을 미칠 수 있습니다. – Serg

+0

"UPDATE pub"및 "INSERT INTO sync_publishers"문의 실제 실행 계획을 SP에서 트리거 대 트리거 확인하십시오. 카디널리티 예측으로 인해 다른 실행 계획을 얻고 있습니까? –

+0

@RazvanSocol, 제안에 감사드립니다! 두 경우의 실제 실행 계획으로 게시물을 업데이트했습니다. 거의 동일하게 보입니다 ... –

답변

0

여기는 병렬로 3 개 작업로드 세션 중 1 시간 이상을 교착 상태로 보이지 않는 버전입니다. 교착 상태의 정확한 이유를 처음부터 알 수는 없지만, 할 수있는 것은 교착 상태가 발생하는 것과의 차이점을 강조하는 것입니다. 또한 병합 성명을 포함하고 있습니다. 버전 확인 (CTE)이 CTE를 사용하여 허용됩니다. MERGEON 표현식은 PK 열 (sync_publisher_id) 만 언급하는 방식으로 다시 작성됩니다.

CREATE PROCEDURE [dbo].[SyncTracker_PublishEvent2] 
    @objectTypeId int, 
    @ids dtInt readonly 
AS 
BEGIN 
    SET NOCOUNT ON; 

    -- stop recoursive propogations 
    if(TRIGGER_NESTLEVEL() > 1) RETURN; 

    declare @date datetime = getutcdate() 

    ;WITH sync_publishers2CTE AS 
    (
     SELECT [sync_publisher_id], 
       [sync_object_id], 
       [sync_registered_object_type_id], 
       [master_update_date] 
      FROM [dbo].[sync_publishers2] 
      WHERE sync_registered_object_type_id = @objectTypeId 
    ) 
    MERGE sync_publishers2CTE WITH (XLOCK) trg 
    USING 
    (
      SELECT sp.sync_publisher_id, 
        s.Value AS sync_object_id, 
        @objectTypeId AS sync_registered_object_type_id, 
        @date AS master_update_date 
       FROM @ids s 
       LEFT JOIN sync_publishers2 sp ON sp.sync_object_id = s.Value 
               AND sp.sync_registered_object_type_id = @objectTypeId 
    ) src 
    ON (trg.sync_publisher_id = src.sync_publisher_id) 
    WHEN MATCHED 
     THEN UPDATE 
      SET trg.master_update_date = src.master_update_date 
    WHEN NOT MATCHED 
     THEN INSERT 
       (sync_object_id, sync_registered_object_type_id, master_update_date) 
      VALUES 
       (sync_object_id, sync_registered_object_type_id, master_update_date); 
END 

샘플 실행 계획 :

exec plan