Я столкнулся с интересной проблемой в системе, где из-за изменения схемы первая транзакция базы данных в одном потоке блокирует выполнение второй транзакции базы данных до истечения времени ожидания.
Чтобы проверить это, я создал тестовую базу данных:
CREATE DATABASE StackOverflow
GO
USE StackOverflow
ALTER DATABASE StackOverflow SET ALLOW_SNAPSHOT_ISOLATION ON
ALTER DATABASE StackOverflow SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE
GO
CREATE TABLE One (
Id int CONSTRAINT pkOne PRIMARY KEY,
A varchar(10) NOT NULL
)
CREATE TABLE Two (
Id int CONSTRAINT pkTwo PRIMARY KEY,
B varchar(10) NOT NULL,
OneId int NOT NULL CONSTRAINT fkTwoToOne REFERENCES One
)
GO
-----------------------------------------------
CREATE TABLE Three (
Id int CONSTRAINT pkThree PRIMARY KEY,
SurrogateId int NOT NULL CONSTRAINT ThreeSurrUnique UNIQUE,
C varchar(10) NOT NULL
)
GO
CREATE TABLE Four (
Id int CONSTRAINT pkFour PRIMARY KEY,
D varchar(10) NOT NULL,
ThreeSurrogateId int NOT NULL CONSTRAINT fkFourToThree REFERENCES Three(SurrogateId)
)
GO
--Seed data
INSERT INTO One (Id, A) VALUES (1, '')
INSERT INTO Three (Id, SurrogateId, C) VALUES (3, 50, '')
В этом первом тесте транзакция, изменяющая строку в таблице Один, запускается, но еще не зафиксирована. Другая транзакция вставляется в таблицу Two, причем столбец ссылается на ту же строку, которая была изменена в первой транзакции в таблице One. Вторая транзакция будет зависать вечно, пока первая транзакция не будет зафиксирована.
Причина, по которой транзакция ожидает, связана с блокировкой ключа LCK_M_S, удерживаемой первой транзакцией.
Во втором тесте транзакция, изменяющая строку в таблице 3, запускается, но еще не зафиксирована, как в первом тесте. Другая транзакция вставляется в таблицу Four, причем столбец ссылается на ту же строку, которая была изменена в первой транзакции в таблице Three. За исключением этого времени, таблица четыре ссылается на суррогатный ключ в таблице три вместо первичного ключа. Транзакция завершена немедленно и не зависит от первой транзакции.
Мне нужна помощь в понимании того, почему последняя транзакция всегда блокируется предыдущей транзакцией при вставке строки в отдельную таблицу, которая ссылается на таблицу, которая была изменена в первой транзакции. Я думаю, что очевидный бесполезный ответ - из-за ограничения внешнего ключа. Но почему? Особенно потому, что это изоляция моментальных снимков, почему последняя транзакция вообще заботится о первой? Строка, на которую она ссылается, уже существует, и внешний ключ может быть легко проверен, что подтверждается вторым тестом, когда внешний ключ, ссылающийся на суррогатный ключ, завершается без препятствий.