为什么删除外键需要很长时间?

car*_*reo 13 foreign-key sql-server sql-server-2016

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

ALTER TABLE MyTable1 DROP CONSTRAINT FK_MyTable1_col1
ALTER TABLE MyTable2 DROP CONSTRAINT FK_MyTable2_col1
ALTER TABLE MyTable2 DROP CONSTRAINT FK_MyTable2_col2
Run Code Online (Sandbox Code Playgroud)

让我感到惊讶的是脚本需要很长时间:平均每个 DROP FK 需要 20 秒。现在,我明白创建 FK 可能是一件大事,因为服务器必须去检查 FK 约束是否从一开始就没有被侵犯,而是下降了?当丢弃需要这么长时间的 FK 时,服务器会做什么?这既是为了我自己的好奇心,也是为了了解是否有办法让事情变得更快。能够删除 FK(不仅仅是禁用它们)将使我在迁移过程中更快,从而最大限度地减少停机时间。

Mik*_*son 14

删除约束需要一个 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 操作无法在表上执行。


Sql*_*ide 6

我将带您通过一个示例,以便您了解为什么需要很长时间。为此测试创建一个空数据库。

CREATE DATABASE [TestFK]
GO
Run Code Online (Sandbox Code Playgroud)

创建 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
Run Code Online (Sandbox Code Playgroud)

在 Person 表上创建外键约束。

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

在两个表中插入一些数据。

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
Run Code Online (Sandbox Code Playgroud)

打开一个新的查询窗口并运行它(查询完成后不要关闭窗口)。

   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
Run Code Online (Sandbox Code Playgroud)

打开另一个查询窗口并运行它。

USE [TestFK]
GO
ALTER TABLE dbo.person DROP CONSTRAINT FK_Person_AddressID
Run Code Online (Sandbox Code Playgroud)

您将看到删除约束将继续运行(等待),现在运行查询以查看为什么它运行的时间更长以及它正在等待什么锁。

SELECT * FROM sys.dm_os_waiting_tasks 
WHERE blocking_session_id IS NOT NULL; 
Run Code Online (Sandbox Code Playgroud)

提交插入操作后,删除约束将立即完成,因为现在 drop 语句可以获得所需的锁。

对于您的情况,您需要确保没有会话持有兼容锁,这将防止删除约束获取必要的锁/锁。

  • 当您的 drop 语句等待完成时,从另一个窗口运行此查询。这会给你你正在等待的东西。从[此处](http://www.sqlskills.com/blogs/paul/updated-sys-dm_os_waiting_tasks-script/)获取查询。它比我在示例中给出的详细信息更多。 (2认同)