인덱스 잠금 순서로 인한 두 가지 업데이트의 SQL Server 교착 상태 쉬운 방법은 무엇입니까? 내부 인덱싱

상태 열도 업데이트되기 때문에 두 개의 업데이트가 있습니다. 하나는 CI를 먼저 잠근 다음 NCI (상태)를 잠급니다. 다른 하나는 NCI에서 U 잠금을 이미 소유하고 있음을 알고 있으므로 CI에서 U 잠금을 얻으려고 시도하기 때문입니다.

이들을 직렬화하는 가장 쉬운 방법은 무엇입니까? 내부 인덱싱 문제이기 때문에 테이블 수준 힌트를 사용하는 것이 이상합니다. 하나의 테이블 만 관련되어 있습니다. UPDLOCK, HOLDLOCK은 해당 테이블에 필요한 모든 인덱스에 자동으로 적용되어 직렬화되도록 강제합니까?

쿼리는 다음과 같습니다.

UPDATE htt_action_log
SET status = 'ABORTED', CLOSED = GETUTCDATE()
WHERE transition_uuid = '{F53ADDDA-E46B-4726-66D8-D7B640B66597}'
AND status = 'OPEN';

하나의 X는 CI의 행 (CREATED 열)을 잠근 다음 상태 열이 포함 된 NCI에서 X 잠금을 시도합니다.

UPDATE htt_action_log
SET status = 'RUNNING {36082BCD-EB52-4358-E3D3-4D96FD5B9F0F} 1360094342'
WHERE action_uuid = (SELECT TOP 1 action_uuid
                     FROM htt_action_log
                     WHERE transition_uuid = '{F53ADDDA-E46B-4726-66D8-D7B640B66597}'
                         AND status = 'OPEN'
                     ORDER BY action_seq)

이 U는 동일한 NCI를 잠급니다. 중첩 쿼리의 경우 업데이트를 위해 CI를 잠급니다.

따라서 순서는 교착 상태를 생성합니다.

가장 쉬운 솔루션은 두 쿼리를 완전히 차단 (즉, 직렬화)하는 것입니다. 가장 쉬운 방법은 무엇입니까? WITH (UPDLOCK, HOLDLOCK)테이블에 대한 참조를 넣으 십시오 (첫 번째와 두 번째 중 하나).

DDL :

참고이 업데이트에는 클라이언트가이 테이블에 더 많은 인덱스를 가지고 있지만 교착 상태 그래프에는 언급되어 있지 않습니다.

CREATE TABLE [dbo].[HTT_ACTION_LOG](
    [ACTION_UUID] [varchar](128) NOT NULL,
    [TRANSITION_UUID] [varchar](128) NOT NULL,
    [STATUS] [varchar](128) NOT NULL,
    [CREATED] [datetime] NOT NULL,
    [CLOSED] [datetime] NULL,
    [ACTION_SEQ] [int] NOT NULL,
    [ACTION_TYPE] [varchar](15) NOT NULL,
    [ACTION_NAME] [varchar](50) NOT NULL,
    [ACTION_RESULT] [varchar](8000) NULL,
    [PENDING_SINCE] [datetime] NULL,
    [ACTION_SQL] [varchar](8000) NULL,
    [ERROR_OK] [int] NULL,
    [ERROR_COND] [varchar](2048) NULL,
    [RETRY] [varchar](128) NULL,
 CONSTRAINT [PK_HTT_ACTION_LOG_1] UNIQUE NONCLUSTERED 
(
    [ACTION_UUID] ASC
)
)

CREATE CLUSTERED INDEX [IK_HTT_ACTION_LOG_2] ON [dbo].[HTT_ACTION_LOG] 
(
    [CREATED] ASC
)

CREATE NONCLUSTERED INDEX [IK_HTT_ACTION_LOG_1] ON [dbo].[HTT_ACTION_LOG] 
(
    [TRANSITION_UUID] ASC,
    [STATUS] ASC
)
INCLUDE ( [ACTION_UUID],
[ACTION_SEQ])

CREATE NONCLUSTERED INDEX [IK_HTT_ACTION_LOG_4] ON [dbo].[HTT_ACTION_LOG] 
(
    [ACTION_UUID] ASC,
    [STATUS] ASC
)

CREATE NONCLUSTERED INDEX [missing_index_11438530_11438529_HTT_ACTION_LOG] ON [dbo].[HTT_ACTION_LOG] 
(
    [TRANSITION_UUID] ASC,
    [ACTION_TYPE] ASC
)
INCLUDE ( [ACTION_NAME])

CREATE NONCLUSTERED INDEX [missing_index_7207590_7207589_HTT_ACTION_LOG] ON [dbo].[HTT_ACTION_LOG] 
(
    [STATUS] ASC
)
INCLUDE ( [CREATED],
[PENDING_SINCE],
[ACTION_NAME])

CREATE NONCLUSTERED INDEX [missing_index_8535421_8535420_HTT_ACTION_LOG] ON [dbo].[HTT_ACTION_LOG] 
(
    [TRANSITION_UUID] ASC
)
INCLUDE ( [ACTION_UUID],
[STATUS])

ALTER TABLE [dbo].[HTT_ACTION_LOG] SET (LOCK_ESCALATION = AUTO)

ALTER TABLE [dbo].[HTT_ACTION_LOG]  WITH CHECK ADD  CONSTRAINT [FK_HTT_ACTION_LOG_1] FOREIGN KEY([TRANSITION_UUID])
REFERENCES [dbo].[HTT_TRANSITION_LOG] ([TRANSITION_UUID])

ALTER TABLE [dbo].[HTT_ACTION_LOG] CHECK CONSTRAINT [FK_HTT_ACTION_LOG_1]

ALTER TABLE [dbo].[HTT_ACTION_LOG] ADD  DEFAULT ('OPEN') FOR [STATUS]

ALTER TABLE [dbo].[HTT_ACTION_LOG] ADD  DEFAULT (getutcdate()) FOR [CREATED]

ALTER TABLE [dbo].[HTT_ACTION_LOG] ADD  DEFAULT ((0)) FOR [ERROR_OK]


답변

두 쿼리에 대한 최적의 인덱스는 기존의 정의에서 멀지 않은 IK_HTT_ACTION_LOG_1인덱스 (추가 ACTION_UUIDint로서 INCLUDE아래의 향상된 인덱스) :

CREATE INDEX nc1
ON dbo.HTT_ACTION_LOG
(
    TRANSITION_UUID,
    STATUS,
    ACTION_SEQ
);

첫 번째 쿼리는 다음과 같습니다

UPDATE dbo.HTT_ACTION_LOG
SET [STATUS] = 'ABORTED', 
    CLOSED = GETUTCDATE()
WHERE
    TRANSITION_UUID = '{F53ADDDA-E46B-4726-66D8-D7B640B66597}'
    AND [STATUS] = 'OPEN';

다음과 같은 실행 계획을 제공합니다.

두 번째 쿼리는 다음과 같이 표현할 수 있습니다.

UPDATE ToUpdate 
SET [STATUS] = 'RUNNING {36082BCD-EB52-4358-E3D3-4D96FD5B9F0F} 1360094342'
FROM
(
    SELECT TOP (1)
        hal.[STATUS]
    FROM dbo.HTT_ACTION_LOG AS hal
    WHERE
        hal.transition_uuid = '{F53ADDDA-E46B-4726-66D8-D7B640B66597}'
        AND hal.[STATUS] = 'OPEN'
    ORDER BY
        hal.ACTION_SEQ ASC
) AS ToUpdate;

이 실행 계획을 제공 :

이제 두 쿼리 모두 동일한 순서로 동일한 리소스에 액세스하면서 계획의 읽기 쪽에서 더 적은 수의 행을 잠급니다. 실행 엔진은 UPDLOCK새 인덱스를 읽을 때 자동으로 사용 하여 찾고있는 직렬화를 제공합니다.