編集:必要に応じて、JDBCを介してJava webappでデータベースに接続します。これらすべての操作にautocommit=off
を使用します。デッドロックは、ジョブスレッドがコミットされていないことを意味します。
SQLServerの断続的なデッドロックの問題を解決しようとしています。デッドロックレポートがあり、2つのクエリがどのように競合しているかを理解できません。
関係するスレッドは2つあります。1つはX
という名前のテーブルでentities
ロックを取得してから、U
という名前のテーブルでrevisions
ロックを取得しようとします。 _。その間、別のスレッドがX
をrevisions
ロックし、U
をentities
ロックしようとしています。この質問では、entities
テーブルに焦点を当てます。
ここで私を混乱させているのは、X
にentities
ロックを作成したクエリを見つけたと思いますが、このロックがU
リクエストとどのように競合するかわかりません。
以下は、Xロッククエリであると私が推測しているクエリの例です。
INSERT INTO entities(
uuid,
entity_type,
internal_id,
date_modified) VALUES (
@P0,
'USER',
null,
getdate()
)
このテーブルには2つのインデックスがあります。主キー(uuid)と一意のインデックス(entity_type、internal_id)です。
デッドロックレポートは、関連するインデックスが(entity_type、internal_id)をカバーする一意のインデックスであると主張しています。テストにより、上記のステートメントが実際にクラスター化インデックス(uuid)を使用して行のXロックを作成することを確認しましたが、一意のインデックスのXロックも作成します。トランザクションを使用してSSMSでこれをテストし、sys.dm_tran_locks
テーブルにクエリを実行して、作成されたロックを確認しました。
ただし、entities
テーブルにUロックを作成しているクエリは、主キーにのみロックを作成するため、そのインデックスをロックしているようには見えません。これがクエリです:
UPDATE entities SET
head_rev_id = @P0,
date_modified = getdate()
WHERE uuid = @P1
両方のクエリにある実際のパラメーターを確認できないことに注意してください。ただし、これらのクエリが最終的にどのように実行されるかというコンテキストに基づいて、それらが同じである可能性がある論理的な理由はわかりません。 (サイドの質問:クエリに渡されるパラメーターを確認する方法はありますか?TRACE 1222を有効にしましたが、拡張イベントにあったデッドロックレポート以外の詳細を取得していません。)試しました2番目のトランザクションで同じエンティティを更新すると、sys.dm_tran_locks
ビューは、クラスター化インデックスのみを使用して、待機状態のXロックを持っていることを示します。
これがデッドロックレポートのxmlです。多分私はこれを間違って読んでいますが、他に何を探すことができますか?
<deadlock>
<victim-list>
<victimProcess id="process476f3dc38" />
</victim-list>
<process-list>
<process id="process476f3dc38" taskpriority="0" logused="1768" waitresource="KEY: 66:72057594135838720 (c3a138dec5f7)" waittime="4831" ownerId="7413985" transactionname="implicit_transaction" lasttranstarted="2020-01-06T17:17:18.320" XDES="0x46f5363a8" lockMode="U" schedulerid="4" kpid="3064" status="suspended" spid="108" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2020-01-06T17:17:18.440" lastbatchcompleted="2020-01-06T17:17:18.437" lastattention="1900-01-01T00:00:00.437" clientapp="Microsoft JDBC Driver for SQL Server" hostname="xxx" hostpid="0" loginname="xxx" isolationlevel="read committed (2)" xactid="7413985" currentdb="66" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
<executionStack>
<frame procname="adhoc" line="1" stmtstart="78" sqlhandle="0x0200000045fed007bf27432ade4d4e079e599714526613ff0000000000000000000000000000000000000000">
UPDATE revisions SET state = @P0 WHERE uuid = @P1 </frame>
<frame procname="unknown" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
(@P0 nvarchar(4000),@P1 nvarchar(4000))UPDATE revisions SET state = @P0 WHERE uuid = @P1 </inputbuf>
</process>
<process id="process476f37c38" taskpriority="0" logused="10568" waitresource="KEY: 66:72057594133544960 (4c37a96d5030)" waittime="4831" ownerId="7413919" transactionname="implicit_transaction" lasttranstarted="2020-01-06T17:17:18.290" XDES="0x4744323a8" lockMode="U" schedulerid="7" kpid="1920" status="suspended" spid="85" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2020-01-06T17:17:18.440" lastbatchcompleted="2020-01-06T17:17:18.440" lastattention="1900-01-01T00:00:00.440" clientapp="Microsoft JDBC Driver for SQL Server" hostname="xxx" hostpid="0" loginname="xxx" isolationlevel="read committed (2)" xactid="7413919" currentdb="66" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
<executionStack>
<frame procname="adhoc" line="1" stmtstart="78" sqlhandle="0x02000000ab882b34a7f1c9e08c7ff4a42d357133d4972a960000000000000000000000000000000000000000">
UPDATE entities SET
head_rev_id = @P0,
date_modified = getdate()
WHERE uuid = @P1 </frame>
<frame procname="unknown" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
(@P0 nvarchar(4000),@P1 nvarchar(4000))UPDATE entities SET
head_rev_id = @P0,
date_modified = getdate()
WHERE uuid = @P1 </inputbuf>
</process>
</process-list>
<resource-list>
<keylock hobtid="72057594135838720" dbid="66" objectname="revisions" indexname="revisions_parent_idx" id="lock2cfaf0e00" mode="X" associatedObjectId="72057594135838720">
<owner-list>
<owner id="process476f37c38" mode="X" />
</owner-list>
<waiter-list>
<waiter id="process476f3dc38" mode="U" requestType="wait" />
</waiter-list>
</keylock>
<keylock hobtid="72057594133544960" dbid="66" objectname="entities" indexname="unique_entities" id="lock2e75ddc80" mode="X" associatedObjectId="72057594133544960">
<owner-list>
<owner id="process476f3dc38" mode="X" />
</owner-list>
<waiter-list>
<waiter id="process476f37c38" mode="U" requestType="wait" />
</waiter-list>
</keylock>
</resource-list>
</deadlock>
問題の2つのテーブルのスキーマは次のとおりです。
CREATE TABLE [entities]
(
[uuid] [varchar](100) NOT NULL,
[entity_type] [varchar](100) NOT NULL,
[internal_id] [varchar](255) NULL,
[date_modified] [datetime2](6) NOT NULL,
[head_rev_id] [varchar](100) NULL,
[test] [varchar](100) NULL,
CONSTRAINT [entities_pk] PRIMARY KEY CLUSTERED
(
[uuid] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON),
CONSTRAINT [unique_entities] UNIQUE NONCLUSTERED
(
[entity_type] ASC,
[internal_id] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)
ALTER TABLE [entities]
WITH CHECK ADD CONSTRAINT [entities_revision_fk] FOREIGN KEY ([head_rev_id])
REFERENCES [revisions] ([uuid])
ALTER TABLE [entities]
WITH CHECK ADD CONSTRAINT [entity_types_entities_fk] FOREIGN KEY ([entity_type])
REFERENCES [entity_types] ([entity_type])
ON DELETE CASCADE
CREATE TABLE [revisions]
(
[entity_id] [varchar](100) NOT NULL,
[uuid] [varchar](100) NOT NULL,
[parent_uuid] [varchar](100) NULL,
[priority] [numeric](20, 0) NOT NULL,
[state] [varchar](50) NOT NULL,
CONSTRAINT [revisions_pk] PRIMARY KEY CLUSTERED
(
[uuid] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)
ALTER TABLE [revisions]
WITH CHECK ADD CONSTRAINT [rev_entity] FOREIGN KEY ([entity_id])
REFERENCES [entities] ([uuid])
ON DELETE CASCADE
ALTER TABLE [revisions]
WITH CHECK ADD CONSTRAINT [rev_rev_state] FOREIGN KEY ([state])
REFERENCES [revision_states] ([state])
CREATE NONCLUSTERED INDEX [revisions_entity_idx] ON [revisions]
(
[entity_id] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
CREATE NONCLUSTERED INDEX [revisions_parent_idx] ON [revisions]
(
[parent_uuid] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
CREATE UNIQUE NONCLUSTERED INDEX [u_duplicate_unassigned] ON [revisions]
(
[entity_id] ASC,
[state] ASC
)
WHERE ([state] = 'UNASSIGNED')
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
以前にこのタイプのデッドロックに遭遇したことがあります。問題は、entities
テーブルに対するrevisions
テーブルへの外部キー検証が、revisions
テーブルへの更新によってブロックされていることです。これは、プロセスの1つを効率化するためのキーがrevisions
にないこと(おそらくuuid
フィールドのキー)が原因で発生し、参照の検索中にPKをスキャンします。これは、インデックスのクロスリクエストページのクエリをブロックします(注:これは私の理論-確認していませんが、あたかも本当であるかのように操作すると、これらの問題が以前に修正されました) 。私はお勧め:
CREATE INDEX Idx_Revisions_uuid ON revisions (uuid)
おそらく、他のINCLUDEデータ、またはインデックスフィールドを使用するのが最善です。これが機能するためには、uuid
フィールドがインデックスの最初のフィールドでなければならないことを覚えておいてください。