Perché la caduta di chiavi esterne richiede molto tempo?


13

Ho creato uno script che, uno alla volta, elimina tutte le chiavi esterne da un database, proprio come questo:

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

Ciò che mi sorprende è che la sceneggiatura richiede molto tempo: in media 20 secondi per ogni DROP FK. Ora, capisco che la creazione di un FK può essere un grosso problema, perché il server deve andare e verificare che il vincolo FK non sia stato violato dall'inizio, ma in calo? Cosa fa un server quando si rilasciano FK che impiegano così tanto tempo? Questo è sia per la mia stessa curiosità, sia per capire se c'è un modo per rendere le cose più veloci. Essere in grado di rimuovere FK (non solo disabilitarli) mi permetterebbe di essere molto più veloce durante una migrazione e quindi ridurre al minimo i tempi di fermo.


1
Forse un altro processo inserisce blocchi dello schema condivisi sul database, costringendo il processo drop FK ad attendere il completamento di tali processi? Prova a eseguire il drop FK e controlla immediatamente sp_who2 per il blocco.
Daniel Hutmacher,

Ho dimenticato di menzionare, non ci sono altri processi in esecuzione su questo database. Ma ci sono altri database sullo stesso server.
carlo.borreo,

Risposte:


12

L'eliminazione di un vincolo richiede un blocco Sch-M (modifica dello schema) che bloccherà gli altri a interrogare la tabella durante la modifica. Probabilmente stai aspettando di ottenere quel blocco e devi aspettare fino al termine di tutte le query attualmente in esecuzione su quella tabella.
Una query in esecuzione ha un blocco Sch-S (Schema Stability) sul tavolo e quel blocco non è compatibile con un blocco Sch-M.

Dalle modalità di blocco, Schema Locks

Motore di database utilizza i blocchi di modifica dello schema (Sch-M) durante un'operazione DDL (Table Data Definition Language), come l'aggiunta di una colonna o l'eliminazione di una tabella. Durante il tempo in cui viene tenuto, il blocco Sch-M impedisce l'accesso simultaneo alla tabella. Ciò significa che il blocco Sch-M blocca tutte le operazioni esterne fino al rilascio del blocco.

Alcune operazioni del linguaggio di manipolazione dei dati (DML), come il troncamento delle tabelle, utilizzano i blocchi Sch-M per impedire l'accesso alle tabelle interessate da operazioni simultanee.

Motore di database utilizza i blocchi di stabilità dello schema (Sch-S) durante la compilazione e l'esecuzione di query. I blocchi Sch-S non bloccano alcun blocco transazionale, inclusi i blocchi esclusivi (X). Pertanto, altre transazioni, incluse quelle con blocchi X su una tabella, continuano a essere eseguite durante la compilazione di una query. Tuttavia, le operazioni DDL simultanee e le operazioni DML simultanee che acquisiscono blocchi Sch-M non possono essere eseguite sulla tabella.


A volte anche l'evidenziazione della tabella in SSMS creerà un Sch-Sblocco e sospetto che questa sia la causa principale dei problemi del PO.
John Eisbrener,

5

Ti guiderò attraverso un esempio così, puoi vedere perché ci è voluto molto tempo. Creazione di un database vuoto per questo test.

CREATE DATABASE [TestFK]
GO

Creazione di 2 tabelle.

 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

Creazione di un vincolo di chiave esterna sulla tabella Persona.

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

Inserisci alcuni dati in entrambe le tabelle.

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

Aprire una nuova finestra della query ed eseguirla (non chiudere la finestra una volta completata la query).

   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

Apri un'altra finestra di query ed esegui questo.

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

Vedrai che il vincolo di rilascio continuerà a funzionare (in attesa) e ora esegui la query per vedere perché è in esecuzione più a lungo e quali blocchi sta aspettando.

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

Dopo aver eseguito l'operazione di inserimento, il vincolo di rilascio verrà completato immediatamente poiché ora l'istruzione di rilascio può acquisire il blocco richiesto.

Nel tuo caso devi assicurarti che nessuna sessione sia in possesso di un blocco compatibile che impedirà il vincolo di caduta per acquisire i blocchi / blocchi necessari.


Nessun altro stava usando il database, ma d'altra parte, non posso escludere di avere una finestra aperta su questo database. Farò un altro esperimento.
carlo.borreo,

1
Quando l'istruzione drop è in attesa di essere terminata, eseguire questa query da un'altra finestra. Questo ti darà quello che stai aspettando. Ottieni la query da qui . Ha più dettagli di quello che ho dato nel mio esempio.
SqlWorldWide,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.