Тупик Использование самореферентного внешнего ключа - PullRequest
2 голосов
/ 05 мая 2011

Использование SQL Server 2005: у меня есть таблица с внешним ссылочным ключом.

Взаимная блокировка возникает, когда обновление и вставка в эту таблицу происходят одновременно, но только при следующих условиях:

  • обновление происходит 1-е
  • обновление относится к записи, на которую будет ссылаться внешний ключ оператора вставки

Во время обновления X-блокировка создается для первичного ключа, который является кластеризованным индексом таблицы. Я попробовал несколько вещей, чтобы попытаться предотвратить это, например:

  • Я пытался изменить уровни изоляции транзакции
  • предоставление табличных подсказок, т. Е. WITH (nolock)
  • Я попытался создать некластеризованный индекс для первичного ключа таблицы, чтобы его можно было использовать вместо кластерного.

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

Спасибо.

Ответы [ 4 ]

0 голосов
/ 22 июня 2011

Я недавно столкнулся с точно такой же проблемой. Как упомянуто в этом посте , для меня решение было просто добавить индекс в колонке с самоссылкой (не в pk). После этого тупики полностью исчезли.

0 голосов
/ 05 мая 2011

Когда запись считается подходящей для UPDATE, на нее помещается замок X (или его страница и т. Д., В зависимости от степени детализации блокировки, выбранной двигателем).

Это предотвращает установку блокировки S на задействованный ресурс, который необходим для получения его значения.

Поскольку для вставки значения в дочернюю таблицу требуется проверка его по родительской таблице, оператору INSERT придется ждать, пока транзакция UPDATE не завершится или не откатится.

Если вы сделаете PRIMARY KEY некластеризованным, UPDATE не должен влиять на него (если только вы не обновляете сам PRIMARY KEY, чего не следует делать в обычных обстоятельствах), поэтому INSERT будет успешным.

Команды ниже работают для меня:

Транзакция 1:

CREATE TABLE parent (id INT NOT NULL PRIMARY KEY NONCLUSTERED, value INT NOT NULL, parentId INT REFERENCES parent)

INSERT
INTO    parent
VALUES  (1, 1, NULL)

BEGIN TRANSACTION
UPDATE  parent
SET     value = 2
WHERE   id = 1

Транзакция 2:

INSERT
INTO    parent
VALUES  (2, 1, 1)
0 голосов
/ 05 мая 2011

Пожалуйста, предоставьте DDL и ваши UPDATE / INSERT заявления. Я не могу воспроизвести это.

CREATE TABLE T
(
id int identity(1,1) primary key,
refid int references T(id),
filler char(10)
)

INSERT INTO T (refid)
select number 
FROM master..spt_values where number between 1 and 2248

Соединение 1

BEGIN TRAN
UPDATE T SET filler = 'A' WHERE id=500  

Соединение 2

BEGIN TRAN
INSERT INTO T (refid) VALUES (500) /*Blocked - No deadlock*/
0 голосов
/ 05 мая 2011

Вы пытались разделить транзакции UPDATE и INSERT на BEGIN TRANSACTION и COMMIT. Таким образом вы избежите тупиков.

Нечто подобное;

BEGIN TRANSACTION insert
  <INSERT SQL>
  COMMIT TRANSACTION insert
  BEGIN TRANSACTION update
     <UPDATE SQL>
  COMMIT TRANSACTION update
END
...