¿Por qué tarda tanto tiempo en soltar claves foráneas?

13

He creado un script que, uno a la vez, elimina todas las claves foráneas de una base de datos, así:

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

Lo que me sorprende es que el guión lleva mucho tiempo: en promedio, 20 segundos por cada DROP FK. Ahora, entiendo que crear un FK puede ser un gran problema, porque el servidor tiene que ir y verificar que la restricción FK no se infringe desde el principio, sino que se cae. ¿Qué hace un servidor al soltar FK que lleva tanto tiempo? Esto es para mi propia curiosidad y para comprender si hay una manera de hacer las cosas más rápido. Ser capaz de eliminar FK (no solo deshabilitarlos) me permitiría ser mucho más rápido durante una migración y, por lo tanto, minimizar el tiempo de inactividad.

carlo.borreo
fuente
1
¿Quizás otro proceso coloca bloqueos de esquema compartidos en su base de datos, forzando al proceso de caída de FK a esperar que esos procesos finalicen? Intente ejecutar el drop FK y luego verifique inmediatamente sp_who2 para el bloqueo.
Daniel Hutmacher
Olvidé mencionar que no hay otros procesos ejecutándose en esta base de datos. Pero hay en otras bases de datos en el mismo servidor.
carlo.borreo

Respuestas:

12

Eliminar una restricción requiere un bloqueo Sch-M (modificación de esquema) que bloqueará a otros para consultar la tabla durante la modificación. Probablemente esté esperando obtener ese bloqueo y tendrá que esperar hasta que finalicen todas las consultas que se ejecutan actualmente en esa tabla.
Una consulta en ejecución tiene un bloqueo Sch-S (Estabilidad de esquema) en la tabla y ese bloqueo es incompatible con un bloqueo Sch-M.

Desde modos de bloqueo, bloqueos de esquema

El Motor de base de datos utiliza bloqueos de modificación de esquema (Sch-M) durante una operación de lenguaje de definición de datos de tabla (DDL), como agregar una columna o descartar una tabla. Durante el tiempo que se mantiene, el bloqueo Sch-M impide el acceso concurrente a la tabla. Esto significa que el bloqueo Sch-M bloquea todas las operaciones externas hasta que se libera el bloqueo.

Algunas operaciones del lenguaje de manipulación de datos (DML), como el truncamiento de tablas, usan bloqueos Sch-M para evitar el acceso a las tablas afectadas por operaciones concurrentes.

El Motor de base de datos utiliza bloqueos de estabilidad de esquema (Sch-S) al compilar y ejecutar consultas. Los bloqueos Sch-S no bloquean ningún bloqueo transaccional, incluidos los bloqueos exclusivos (X). Por lo tanto, otras transacciones, incluidas aquellas con bloqueos X en una tabla, continúan ejecutándose mientras se compila una consulta. Sin embargo, las operaciones DDL concurrentes y las operaciones DML concurrentes que adquieren bloqueos Sch-M no se pueden realizar en la tabla.

Mikael Eriksson
fuente
A veces, incluso resaltar la tabla en SSMS creará un Sch-Sbloqueo, y sospecho que esta es la causa raíz de los problemas del OP.
John Eisbrener
5

Te explicaré un ejemplo para que puedas ver por qué estaba tomando tanto tiempo. Crear una base de datos vacía para esta prueba.

CREATE DATABASE [TestFK]
GO

Creando 2 tablas.

 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

Creación de una restricción de clave externa en la tabla Persona.

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

Inserte algunos datos en ambas tablas.

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

Abra una nueva ventana de consulta y ejecútela (no cierre la ventana una vez que se complete la consulta).

   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

Abra otra ventana de consulta y ejecute esto.

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

Verá que la restricción de caída continuará ejecutándose (esperando) y ahora ejecutará la consulta para ver por qué se está ejecutando por más tiempo y qué bloqueos está esperando.

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

Una vez que confirme su operación de inserción, la restricción de caída se completará inmediatamente porque ahora la declaración de caída puede adquirir el bloqueo requerido.

Para su caso, debe asegurarse de que ninguna sesión tenga un bloqueo compatible que evite la restricción de caída para adquirir los bloqueos / bloqueos necesarios.

SqlWorldWide
fuente
Nadie más estaba usando la base de datos, pero por otro lado, no puedo excluir que tenía una ventana abierta en esta base de datos. Haré otro experimento.
carlo.borreo
1
Cuando su declaración de caída esté esperando para finalizar, ejecute esta consulta desde otra ventana. Eso te dará lo que estás esperando. Obtenga la consulta desde aquí . Tiene más detalles que los que di en mi ejemplo.
SqlWorldWide