外部キーの削除に時間がかかるのはなぜですか?


13

次のように、一度に1つずつ、データベースからすべての外部キーを削除するスクリプトを作成しました。

ALTER TABLE MyTable1 DROP CONSTRAINT FK_MyTable1_col1
ALTER TABLE MyTable2 DROP CONSTRAINT FK_MyTable2_col1
ALTER TABLE MyTable2 DROP CONSTRAINT FK_MyTable2_col2

私が驚いたのは、スクリプトに時間がかかることです。DROPFKごとに平均20秒です。今、私はFKを作成することは大したことかもしれないことを理解しています、なぜならサーバーはFK制約が最初から侵害されていないことを確認しなければならないからです?時間がかかるFKをドロップするとき、サーバーは何をしますか?これは私自身の好奇心のためであり、物事をより速くする方法があるかどうかを理解するためです。FKを(単に無効にするだけでなく)削除できると、移行中にはるかに高速になり、したがってダウンタイムを最小限に抑えることができます。


1
別のプロセスがデータベースに共有スキーマロックを設定し、ドロップFKプロセスにそれらのプロセスが終了するのを待つように強制するかもしれません。ドロップFKを実行し、すぐにsp_who2のブロックを確認してください。
ダニエルハットマッハー

言及するのを忘れましたが、このデータベースで実行されている他のプロセスはありません。ただし、同じサーバー上の他のデータベースにはあります。
carlo.borreo

回答:


12

制約を削除するには、Sch-M(スキーマ変更)ロックが必要です。これは、変更中に他の人がテーブルを照会するのをブロックします。あなたはおそらくそのロックを取得するのを待っていて、そのテーブルに対して現在実行中のすべてのクエリが完了するまで待たなければなりません。
実行中のクエリには、テーブルにSch-S(スキーマ安定性)ロックがあり、そのロックはSch-Mロックと互換性がありません。

ロックモードから、スキーマロック

データベースエンジンは、列の追加やテーブルの削除など、テーブルデータ定義言語(DDL)操作中にスキーマ変更(Sch-M)ロックを使用します。保持されている間、Sch-Mロックはテーブルへの同時アクセスを防ぎます。これは、ロックが解除されるまでSch-Mロックがすべての外部操作をブロックすることを意味します。

テーブルの切り捨てなどの一部のデータ操作言語(DML)操作では、Sch-Mロックを使用して、同時操作による影響を受けるテーブルへのアクセスを防ぎます。

データベースエンジンは、クエリのコンパイルおよび実行時にスキーマ安定性(Sch-S)ロックを使用します。Sch-Sロックは、排他的(X)ロックを含むトランザクションロックをブロックしません。したがって、テーブルにXロックが設定されているトランザクションを含む他のトランザクションは、クエリのコンパイル中も引き続き実行されます。ただし、同時DDL操作、およびSch-Mロックを取得する同時DML操作は、テーブルに対して実行できません。


SSMSでテーブルを強調表示してもSch-Sロックが作成されることがありますが、これがOPの問題の根本原因であると思われます。
ジョンEisbrener

5

例を挙げて説明しますので、なぜ時間がかかったのかがわかります。このテスト用に空のデータベースを作成します。

CREATE DATABASE [TestFK]
GO

2つのテーブルを作成します。

 USE [TestFK]
 GO
CREATE TABLE dbo.[Address] (
      ADDRESSID   INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
       Address1    VARCHAR(50),
      City        VARCHAR(50),
      [State]     VARCHAR(10),
      ZIP     VARCHAR(10));
GO

CREATE TABLE dbo.Person (
       PersonID    INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
       LastName    VARCHAR(50) NOT NULL,
     FirstName   VARCHAR(50),
      AddressID   INT);
GO

Personテーブルに外部キー制約を作成します。

 USE [TestFK]
 GO
ALTER TABLE dbo.Person ADD CONSTRAINT FK_Person_AddressID FOREIGN KEY (AddressID)
REFERENCES dbo.Address(AddressID)
GO

両方のテーブルにデータを挿入します。

USE [TestFK]
GO
INSERT dbo.Address (Address1,City,[State],Zip)
  SELECT '123 Easy St','Austin','TX','78701'
    UNION
 SELECT '456 Lakeview','Sunrise Beach','TX','78643'
GO
INSERT dbo.Person (LastName,FirstName,AddressID)
    SELECT 'Smith','John',1
   UNION
 SELECT 'Smith','Mary',1
   UNION
 SELECT 'Jones','Max',2
GO

新しいクエリウィンドウを開き、これを実行します(クエリが完了したらウィンドウを閉じないでください)。

   USE [TestFK]
   GO
   BEGIN TRAN
   INSERT dbo.Person (LastName,FirstName,AddressID)
    SELECT 'Smith1','John1',1
    UNION
    SELECT 'Smith1','Mary1',1
    UNION
    SELECT 'Jones1','Max1',2

別のクエリウィンドウを開き、これを実行します。

USE [TestFK]
GO
ALTER TABLE dbo.person DROP CONSTRAINT FK_Person_AddressID

ドロップ制約が実行され続ける(待機)のを確認し、クエリを実行して、なぜ長く実行されているのか、どのロックが待機しているのかを確認します。

SELECT * FROM sys.dm_os_waiting_tasks 
WHERE blocking_session_id IS NOT NULL; 

挿入操作をコミットすると、ドロップ文は必要なロックを取得できるため、ドロップ制約はすぐに完了します。

あなたの場合、セッションが互換性のあるロックを保持していないことを確認する必要があります。これにより、ドロップ制約が必要なロックを取得できなくなります。


誰もデータベースを使用していませんでしたが、一方で、このデータベースで開いているウィンドウがあったことを除外することはできません。別の実験を行います。
carlo.borreo

1
ドロップステートメントが終了するのを待っているときに、別のウィンドウからこのクエリを実行します。それはあなたが待っているものをあなたに与えるでしょう。ここからクエリを取得します。例で示したものよりも詳細があります。
-SqlWorldWide
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.