Por que soltar chaves estrangeiras leva muito tempo?

13

Eu criei um script que, um de cada vez, exclui todas as chaves estrangeiras de um banco de dados, assim:

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

O que me surpreende é que o script leva muito tempo: em média, 20 segundos para cada DROP FK. Agora, eu entendo que criar um FK pode ser um grande problema, porque o servidor precisa verificar se a restrição do FK não foi violada desde o início, mas caiu? O que um servidor faz ao eliminar FKs que demoram tanto tempo? Isso é tanto para minha própria curiosidade quanto para entender se existe uma maneira de tornar as coisas mais rápidas. Ser capaz de remover o FK (não apenas desativá-los) me permitiria ser muito mais rápido durante uma migração e, portanto, minimizar o tempo de inatividade.

carlo.borreo
fonte
1
Talvez outro processo coloque bloqueios de esquema compartilhados em seu banco de dados, forçando o processo de queda do FK a aguardar a conclusão desses processos? Tente executar o drop FK e verifique imediatamente sp_who2 quanto a bloqueio.
Daniel Hutmacher
Esqueci de mencionar, não há outros processos em execução neste banco de dados. Mas existem em outros bancos de dados no mesmo servidor.
Carlo.borreo 12/04/19

Respostas:

12

A eliminação de uma restrição requer um bloqueio Sch-M (modificação do esquema) que bloqueia outras pessoas para consultar a tabela durante a modificação. Você provavelmente está aguardando para obter esse bloqueio e deve aguardar até que todas as consultas atualmente em execução nessa tabela sejam concluídas.
Uma consulta em execução possui um bloqueio Sch-S (Schema Stability) na tabela e esse bloqueio é incompatível com um bloqueio Sch-M.

Dos modos de bloqueio, bloqueios de esquema

O Mecanismo de Banco de Dados usa bloqueios de modificação de esquema (Sch-M) durante uma operação de DDL (tabela de definição de dados de tabela), como adicionar uma coluna ou descartar uma tabela. Durante o tempo em que é mantido, o bloqueio Sch-M impede o acesso simultâneo à tabela. Isso significa que a trava Sch-M bloqueia todas as operações externas até que a trava seja liberada.

Algumas operações da linguagem de manipulação de dados (DML), como truncamento de tabela, usam bloqueios Sch-M para impedir o acesso às tabelas afetadas por operações simultâneas.

O Mecanismo de Banco de Dados usa bloqueios de estabilidade do esquema (Sch-S) ao compilar e executar consultas. Os bloqueios Sch-S não bloqueiam nenhum bloqueio transacional, incluindo bloqueios exclusivos (X). Portanto, outras transações, incluindo aquelas com bloqueios X em uma tabela, continuam sendo executadas enquanto uma consulta está sendo compilada. No entanto, operações DDL simultâneas e operações DML simultâneas que adquirem bloqueios Sch-M, não podem ser executadas na tabela.

Mikael Eriksson
fonte
Às vezes, até o destaque da tabela no SSMS cria um Sch-Sbloqueio, e eu suspeito que essa seja a causa raiz dos problemas do OP.
John Eisbrener
5

Vou orientá-lo através de um exemplo para que você possa ver por que estava demorando muito tempo. Criando um banco de dados vazio para este teste.

CREATE DATABASE [TestFK]
GO

Criando 2 tabelas.

 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

Criando uma restrição de Chave Estrangeira na tabela Pessoa.

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

Insira alguns dados nas duas tabelas.

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 uma nova janela de consulta e execute-a (não feche a janela depois que a consulta for concluída).

   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 outra janela de consulta e execute isso.

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

Você verá que a restrição de queda continuará em execução (aguardando) e agora execute a consulta para ver por que está demorando mais e quais bloqueios está aguardando.

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

Depois de confirmar sua operação de inserção, a restrição de descarte será concluída imediatamente, porque agora a instrução de descarte pode adquirir o bloqueio necessário.

Para o seu caso, você precisa garantir que nenhuma sessão esteja mantendo um bloqueio compatível, o que impedirá a restrição de queda para adquirir os bloqueios / bloqueios necessários.

SqlWorldWide
fonte
Ninguém mais estava usando o banco de dados, mas, por outro lado, não posso excluir que eu tinha uma janela aberta nesse banco de dados. Eu farei outro experimento.
Carlo.borreo 12/04
1
Quando a instrução drop estiver aguardando para ser concluída, execute esta consulta em outra janela. Isso lhe dará o que você está esperando. Obtenha a consulta daqui . Ele tem mais detalhes do que eu dei no meu exemplo.
SqlWorldWide