web-dev-qa-db-ja.com

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

次のように、一度に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

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

13
carlo.borreo

制約を削除するには、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操作は、テーブルで実行できません。

12
Mikael Eriksson

例を紹介して、長い時間がかかった理由を理解できるようにします。このテスト用に空のデータベースを作成します。

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; 

挿入操作をコミットすると、dropステートメントが必要なロックを取得できるようになるため、ドロップ制約はすぐに完了します。

あなたのケースでは、ドロップ制約が必要なロックを取得するのを防ぐ互換ロックを保持しているセッションがないことを確認する必要があります。

5
SqlWorldWide