patternsqlModerate
SQL Server Deadlock on two updates due to index lock order
Viewed 0 times
dueordersqldeadlocktwoserverindexupdateslock
Problem
I have two UPDATEs - one locks the CI first and then the NCI (on status) because the status column is also being updated. The other already owns a U lock on the NCI because it knows it is changing and then tries to get a U lock on the CI.
What is the easiest way to force these to serialize? It seems odd to use a TABLE-level hint since this is an internal indexing issue - there is only one table involved - will UPDLOCK, HOLDLOCK automatically just apply to all indexes needed on that table and thereby force it to be serialized?
Here are the queries:
That one X locks the row in the CI (on CREATED column) and then attempts to X lock on the NCI which includes the status column.
This one U locks the same NCI - for the nested query I guess, then goes to lock the CI for the update.
Thus the order produces the deadlock.
Easiest solution is to force the two queries to completely block - i.e. serialize. What's the easiest way to force that, just put
DDL:
Note client has more indexes on this table which should be affected by this update, but are not mentioned in the deadlock graph.
```
CREATE TABLE [dbo].HTT_ACTION_LOG NOT NULL,
[TRANSITION_UUID] varchar NOT NULL,
[STATUS] varchar NOT NULL,
[CREATED] [datetime] NOT NULL,
[CLOSED] [datetime] NULL,
[ACTION_SEQ] [int] NOT NULL,
[AC
What is the easiest way to force these to serialize? It seems odd to use a TABLE-level hint since this is an internal indexing issue - there is only one table involved - will UPDLOCK, HOLDLOCK automatically just apply to all indexes needed on that table and thereby force it to be serialized?
Here are the queries:
UPDATE htt_action_log
SET status = 'ABORTED', CLOSED = GETUTCDATE()
WHERE transition_uuid = '{F53ADDDA-E46B-4726-66D8-D7B640B66597}'
AND status = 'OPEN';That one X locks the row in the CI (on CREATED column) and then attempts to X lock on the NCI which includes the status column.
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)This one U locks the same NCI - for the nested query I guess, then goes to lock the CI for the update.
Thus the order produces the deadlock.
Easiest solution is to force the two queries to completely block - i.e. serialize. What's the easiest way to force that, just put
WITH (UPDLOCK, HOLDLOCK) on the references to the table (one in the first and two in the second)?DDL:
Note client has more indexes on this table which should be affected by this update, but are not mentioned in the deadlock graph.
```
CREATE TABLE [dbo].HTT_ACTION_LOG NOT NULL,
[TRANSITION_UUID] varchar NOT NULL,
[STATUS] varchar NOT NULL,
[CREATED] [datetime] NOT NULL,
[CLOSED] [datetime] NULL,
[ACTION_SEQ] [int] NOT NULL,
[AC
Solution
The optimal index for those two queries is not far from the existing definition of the
The first query is:
Giving the following execution plan:
The second query can be expressed this way:
Giving this execution plan:
Both queries now access the same resources in the same order, while locking many fewer rows on the read side of the plan. The execution engine will automatically take
IK_HTT_ACTION_LOG_1 index (add ACTION_UUID as an INCLUDE to the improved index below):CREATE INDEX nc1
ON dbo.HTT_ACTION_LOG
(
TRANSITION_UUID,
STATUS,
ACTION_SEQ
);The first query is:
UPDATE dbo.HTT_ACTION_LOG
SET [STATUS] = 'ABORTED',
CLOSED = GETUTCDATE()
WHERE
TRANSITION_UUID = '{F53ADDDA-E46B-4726-66D8-D7B640B66597}'
AND [STATUS] = 'OPEN';Giving the following execution plan:
The second query can be expressed this way:
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;Giving this execution plan:
Both queries now access the same resources in the same order, while locking many fewer rows on the read side of the plan. The execution engine will automatically take
UPDLOCKs when reading the new index, providing the serialization you are looking for.Code Snippets
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;Context
StackExchange Database Administrators Q#34199, answer score: 13
Revisions (0)
No revisions yet.