我有两个更新 - 一个首先锁定 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 列上),然后尝试 X 锁定包括状态列的 NCI。
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_UUID
到INCLUDE
下面的改进索引中):第一个查询是:
给出以下执行计划:
第二个查询可以这样表示:
给出这个执行计划:
这两个查询现在以相同的顺序访问相同的资源,同时在计划的读取端锁定更少的行。执行引擎在读取新索引时会自动取
UPDLOCK
s,提供您正在寻找的序列化。