Почему удаление внешних ключей занимает много времени?


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

Что меня удивляет, так это то, что сценарий занимает много времени: в среднем 20 секунд для каждого DROP FK. Теперь я понимаю, что создание FK может иметь большое значение, потому что сервер должен пойти и проверить, что ограничение FK не нарушается с самого начала, а отбрасывается? Что делает сервер при отбрасывании FK, что занимает так много времени? Это как для моего собственного любопытства, так и для понимания, есть ли способ сделать вещи быстрее. Возможность удалить FK (а не просто отключить их) позволила бы мне быть намного быстрее во время миграции и, следовательно, минимизировать время простоя.


1
Может быть, другой процесс устанавливает блокировки общей схемы в вашей базе данных, заставляя процесс удаления FK ожидать завершения этих процессов? Попробуйте запустить сброс FK, а затем немедленно проверить sp_who2 на предмет блокировки.
Даниэль

Я забыл упомянуть, что в этой базе данных нет других процессов. Но есть и на других базах данных на этом же сервере.
carlo.borreo

Ответы:


12

Для удаления ограничения требуется блокировка Sch-M (изменение схемы), которая будет блокировать других для запроса таблицы во время модификации. Вы, вероятно, ожидаете, чтобы получить эту блокировку, и должны подождать, пока все текущие запросы к этой таблице не будут завершены.
Запущенный запрос имеет блокировку Sch-S (стабильность схемы) для таблицы, и эта блокировка несовместима с блокировкой Sch-M.

Из режимов блокировки, блокировки схемы

Компонент Database Engine использует блокировки модификации схемы (Sch-M) во время операции языка определения данных таблицы (DDL), такой как добавление столбца или удаление таблицы. Во время удержания блокировка Sch-M предотвращает одновременный доступ к столу. Это означает, что блокировка Sch-M блокирует все внешние операции, пока блокировка не будет снята.

Некоторые операции на языке манипулирования данными (DML), такие как усечение таблиц, используют блокировки Sch-M для предотвращения доступа к затронутым таблицам параллельными операциями.

Компонент Database Engine использует блокировки стабильности схемы (Sch-S) при компиляции и выполнении запросов. Блокировки Sch-S не блокируют никакие транзакционные блокировки, включая эксклюзивные (X) блокировки. Поэтому другие транзакции, включая транзакции с X-блокировками для таблицы, продолжают выполняться во время компиляции запроса. Однако одновременные операции DDL и параллельные операции DML, которые получают блокировки Sch-M, не могут быть выполнены для таблицы.


Иногда даже выделение таблицы в SSMS создает Sch-Sблокировку, и я подозреваю, что это является основной причиной проблем OP.
Джон Эйсбренер,

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

Создание ограничения внешнего ключа для таблицы Person.

 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; 

После того, как вы подтвердите операцию вставки, ограничение сброса будет немедленно выполнено, потому что теперь оператор drop может получить требуемую блокировку.

В вашем случае вам нужно убедиться, что ни один сеанс не удерживает совместимую блокировку, которая будет препятствовать сбросу ограничения для получения необходимых блокировок / блокировок.


Никто другой не использовал базу данных, но, с другой стороны, я не могу исключить, что у меня было открытое окно в этой базе данных. Я сделаю еще один эксперимент.
carlo.borreo

1
Когда ваш оператор отбрасывания ожидает завершения, запустите этот запрос из другого окна. Это даст вам то, что вы ждете. Получить запрос отсюда . В нем больше деталей, чем в моем примере.
SqlWorldWide
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.