为什么丢弃外键要花很长时间?


13

我制作了一个脚本,可以一次删除一个数据库中的所有外键,就像这样:

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(而不仅仅是禁用它们)可以使我在迁移过程中更快,从而最大程度地减少停机时间。


1
也许另一个进程在您的数据库上放置了共享模式锁,从而迫使drop FK进程等待这些进程完成?尝试运行drop FK,然后立即检查sp_who2是否存在阻塞。
Daniel Hutmacher

我忘了提及,此数据库上没有其他进程在运行。但是同一台服务器上还有其他数据库。
carlo.borreo

Answers:


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问题的根本原因。
John 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

在人员表上创建外键约束。

 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
当您的drop语句等待完成时,请从另一个窗口运行此查询。那会给你你在等待什么。从这里获取查询。它比我在示例中提供的详细信息更多。
SqlWorldWide
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.