Параллельные обновления вызывают тупик, когда нет кластерного индекса в столбце условия where - PullRequest
1 голос
/ 07 февраля 2020

Мы столкнулись со сценарием, в котором возникает тупик при попытке обновить таблицу из двух одновременных подключений дважды в рамках одной транзакции и воспроизводить ее каждый раз, когда выполняется запрос 2 * windows в SSMS. (Столбец AccountId является некластеризованным ключом)

См. Ниже.

enter image description here

После создания кластеризованного ключа в столбце AccountId тупик больше не возникает. Что вызывает это поведение?

Ответы [ 2 ]

2 голосов
/ 07 февраля 2020

Без кластеризованного индекса для AccountId и некластеризованного индекса в этом столбце, SQL Сервер должен заблокировать ключ индекса и затем строку.

Таким образом, 1-е обновление будет успешным, и после обновления у вас будет только один строка заблокирована в таблице.
2-е обновление попытается заблокировать эти строки и будет ожидать снятия блокировки с 1-го обновления. Он сможет получить блокировку ключа для индекса.
3-е обновление попытается заблокировать ключ индекса и будет ожидать снятия блокировки со 2-го обновления. Deadlock.

Мне удалось воспроизвести его, используя следующую таблицу:

create table test5 (x int,y int)
insert into test5 values (10,15)
GO

insert into test5 values (11,15)
GO 10000

create index ix on test5(x)

select * from test5

begin transaction

update test5
set y = 5
where x = 10

-- wait here

update test5
set y = 5
where x = 10

rollback

1 голос
/ 07 февраля 2020

План выполнения имеет некластеризованный поиск индекса, выводящий RID базовой таблицы (Bmk1000) и оператор UPDATE, использующий поиск RID. Это обеспечивает два ресурса, участвующих в взаимоблокировке (некластеризованный индексный ключ и строка базовой таблицы, соответствующие RID).

  • Транзакция 1 принимает блокировку U для некластеризованного индексного ключа для AccountId = 1000, затем получает блокировку U в строке базовой таблицы, преобразует ее в блокировку X и освобождает блокировку U для некластерного ключа индекса. Блокировка X удерживается до конца транзакции.
  • Транзакция 2 использует блокировку U для некластеризованного индексного ключа для AccountId = 1000, но блокируется при попытке получить блокировку U для строки базовой таблицы с помощью блокировки X транзакции 1.
  • Транзакция 1 запускает свою вторую UPDATE и пытается получить блокировку U для некластеризованного индексного ключа для AccountId = 1000. Это уже удерживается транзакцией 2. Deadlock.
...