web-dev-qa-db-ja.com

主キーの非クラスター化インデックスは削除を高速化し、デッドロックを防止しますか?

非常に高トラフィックのデータベースがあります。アプリケーションでは、それらは削除を発行し、これらの削除は同じテーブルの他の削除とデッドロックすることがよくあります。私はこれを修正する方法を研究しています。そして、一つの答え saw は、削除がレコードへの最速の経路を確実にすることでした。

現在、すべての削除はこのフォームに従います。

(@0 int) delete [dbo].[table] where (table_id = @0)

これらの各テーブルには、table_idに主キーとクラスター化インデックスがあります。

私の質問は、table_idに非クラスター化インデックスを追加すると、これらの削除を高速化し、デッドロックの発生を防ぐことができるでしょうか?


デッドロックグラフ:

<deadlock victim="process2b53a408c8"><process-list><process id="process2b53a408c8" taskpriority="0" logused="284" waitresource="KEY: 19:72057597368270848 (0e2c6d2527ac)" waittime="2034" ownerId="14899585071" transactionname="user_transaction" lasttranstarted="2018-03-07T09:47:18.833" XDES="0x50746b1c0" lockMode="S" schedulerid="7" kpid="967168" status="suspended" spid="185" sbid="2" ecid="0" priority="0" trancount="2" lastbatchstarted="2018-03-07T09:47:18.850" lastbatchcompleted="2018-03-07T09:47:18.850" lastattention="1900-01-01T00:00:00.850" clientapp="redacted_service" hostname="redacted_Host" hostpid="164656" loginname="redacted_domain\_SQL_redacted_database_P" isolationlevel="read committed (2)" xactid="14899585071" currentdb="19" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056" databaseName="redacted_database"><executionStack><frame procname="adhoc" line="1" stmtstart="16" stmtend="120" sqlhandle="0x020000001335f1027371c186c8d7405191cdef00ddd0ebe70000000000000000000000000000000000000000">
unknown     </frame><frame procname="unknown" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown     </frame></executionStack><inputbuf>
(@0 int)DELETE [dbo].[redacted_table]
WHERE ([id] = @0)    </inputbuf></process><process id="process218b0a5468" taskpriority="0" logused="284" waitresource="KEY: 19:72057597368270848 (c94c2713ec0f)" waittime="2036" ownerId="14899585063" transactionname="user_transaction" lasttranstarted="2018-03-07T09:47:18.833" XDES="0x1fcd0c4d10" lockMode="S" schedulerid="4" kpid="962372" status="suspended" spid="384" sbid="2" ecid="0" priority="0" trancount="2" lastbatchstarted="2018-03-07T09:47:18.850" lastbatchcompleted="2018-03-07T09:47:18.847" lastattention="1900-01-01T00:00:00.847" clientapp="redacted_service" hostname="redacted_Host" hostpid="164656" loginname="redacted_domain\_SQL_redacted_database_P" isolationlevel="read committed (2)" xactid="14899585063" currentdb="19" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056" databaseName="redacted_database"><executionStack><frame procname="adhoc" line="1" stmtstart="16" stmtend="120" sqlhandle="0x020000001335f1027371c186c8d7405191cdef00ddd0ebe70000000000000000000000000000000000000000">
unknown     </frame><frame procname="unknown" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown     </frame></executionStack><inputbuf>
(@0 int)DELETE [dbo].[redacted_table]
WHERE ([id] = @0)    </inputbuf></process></process-list><resource-list><keylock hobtid="72057597368270848" dbid="19" objectname="redacted_database.dbo.redacted_table_2" indexname="PK_redacted_table_2" id="lock9d21dab80" mode="X" associatedObjectId="72057597368270848"><owner-list><owner id="process218b0a5468" mode="X" /></owner-list><waiter-list><waiter id="process2b53a408c8" mode="S" requestType="wait" /></waiter-list></keylock><keylock hobtid="72057597368270848" dbid="19" objectname="redacted_database.dbo.redacted_table_2" indexname="PK_redacted_table_2" id="lock4de4b1800" mode="X" associatedObjectId="72057597368270848"><owner-list><owner id="process2b53a408c8" mode="X" /></owner-list><waiter-list><waiter id="process218b0a5468" mode="S" requestType="wait" /></waiter-list></keylock></resource-list></deadlock>

テーブル定義:

create table [dbo].[redacted_table](
    [id] [int] identity(1,1) not for replication not null,
    [loan_id] [int] not null,
    [user_role_id] [int] not null,
    [assigned_by_user_id] [int] not null,
    [out_for_assignment] [bit] not null,
    [assignment_date] [datetime] not null,
    [recognize_date] [datetime] not null,
    [routing_source] [varchar](50) null,
    [request_guid] [uniqueidentifier] null,
 constraint [PK_redacted_table] primary key clustered 
(
    [id] asc
)with (pad_index = off, statistics_norecompute = off, ignore_dup_key = off, allow_row_locks = on, allow_page_locks = on, fillfactor = 90) on [PRIMARY]
) on [PRIMARY]

go

alter table [dbo].[redacted_table] add  constraint [DF_redacted_table_assignment_date]  default (getdate()) for [assignment_date]
go

alter table [dbo].[redacted_table] add  constraint [DF_redacted_table_recognize_date]  default (getdate()) for [recognize_date]
go

alter table [dbo].[redacted_table]  with check add  constraint [FK_redacted_table_redacted_table3] foreign key([loan_id])
references [dbo].[redacted_table3] ([id])
go

alter table [dbo].[redacted_table] check constraint [FK_redacted_table_redacted_table3]
go

alter table [dbo].[redacted_table]  with check add  constraint [FK_redacted_table_user_redacted_table4] foreign key([user_role_id])
references [dbo].[user_redacted_table4] ([id])
go

alter table [dbo].[redacted_table] check constraint [FK_redacted_table_user_redacted_table4]
go

alter table [dbo].[redacted_table]  with check add  constraint [FK_redacted_table_redacted_table5] foreign key([assigned_by_user_id])
references [dbo].[redacted_table5] ([id])
go

alter table [dbo].[redacted_table] check constraint [FK_redacted_table_redacted_table5]
go
2
DForck42

ステートメントが主キー_table_id_のWHERE句を介して行を削除しているため、_table_id_に非クラスター化インデックスを追加しても効果があり、数が増える可能性がありますデッドロックが発生しています。

_DELETE FROM ..._は、テーブル(またはクラスター化インデックス)から行を削除し、行が存在するテーブルで定義されたすべての非クラスター化インデックスも削除します。追加のインデックスを追加する必要があり、追加の削除操作が必要です。

この非常に単純な例を見てみましょう:

_IF OBJECT_ID(N'dbo.DeleteTest', N'U') IS NOT NULL
DROP TABLE dbo.DeleteTest;
GO
CREATE TABLE dbo.DeleteTest
(
    table_id int NOT NULL
        CONSTRAINT PK_DeleteTest
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , someVal varchar(2000) NOT NULL
        CONSTRAINT DF_DeleteTest_someVal
        DEFAULT ((CRYPT_GEN_RANDOM(2000)))
);
GO

--insert 500 rows
INSERT INTO dbo.DeleteTest DEFAULT VALUES;
GO 500

--turn on "Actual" execution plans
DELETE 
FROM dbo.DeleteTest 
WHERE table_id = 90;
_

上記の実際の実行計画DELETE

enter image description here _CREATE TABLE_ステートメントをデッドロックグラフを介してデッドロックの詳細と共に質問に追加する必要があります。

ここで、追加の非クラスター化インデックスを追加し、別の削除を実行すると、次のようになります。

_CREATE INDEX IX_DeleteTest
ON dbo.DeleteTest (table_id);

DELETE 
FROM dbo.DeleteTest 
WHERE table_id = 91;
_

次の計画が表示されます。

enter image description here

2つの画像からわかるように、2番目の削除のコストはほぼ2倍です。最初の削除の場合は0.0132841、2番目の削除の場合は0.0232851。

SentryOne Plan Explorer (無料!)を使用して実行プランを確認すると、実行されている追加の非クラスター化インデックス削除操作の数を確認できます。

enter image description here

デッドロックグラフを、質問の非クラスター化インデックスのリストと組み合わせると、同じページに含まれる行の同時削除が原因である可能性が高いようです。 ROWLOCKヒントを削除に追加すると、この特定のデッドロックを防ぐことができます。削除操作の前に_SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;_を使用してテストすることもできます。同じバッチ内で後で発生するすべてのステートメントの分離レベルが変更されることに注意してください。削除操作後に他のステートメントを実行する場合は、トランザクション分離レベルをリセットしてください。

デッドロックは、行番号を除いて、待機リソースが両方のステートメントで同じであることを示しています。waitresource="KEY: 19:72057597368270848 (0e2c6d2527ac)"行番号は、括弧内の番号です。 @Kinは、彼のwaitresourceの詳細を確認するための優れた方法を示しています ここで回答

[〜#〜]行ロック[〜#〜]
通常、ページまたはテーブルのロックが行われるときに行ロックが行われることを指定します。 SNAPSHOT分離レベルで動作するトランザクションで指定されている場合、ROWLOCKがUPDLOCKやHOLDLOCKなどのロックを必要とする他のテーブルヒントと組み合わされていない限り、行ロックは取得されません。

8
Max Vernon

私はそうは思いません。単純なテーブルでテストし、クラスター化されていない変更されたクエリプランを、より見た目が悪いものに変更しました。

リーチとヒントは、多くの場合、進むべき道ではありませんが、

delete [Table_1] with (rowlock) where pk = @pk;

テーブルへの外部キーをチェックする必要があります。

0
paparazzo