SQL Server 2005 тупик с некластеризованным индексом - PullRequest
10 голосов
/ 20 января 2010

Может кто-нибудь помочь мне в тупике в SQL Server 2005?

Для простого теста у меня есть таблица «Книга», в которой есть первичный ключ (id) и имя столбца. Индекс по умолчанию для этого первичного ключа: некластеризованный .

тупик возникает, когда две сессии запускаются одновременно. Монитор активности показывает, что первый сеанс "// шаг 1" блокирует строку (освобождает блокировку) с помощью X-блокировки. Второй сеанс сохраняет блокировку строки U и блокировку ключа U. На рисунке тупика показано, что «// step2» первого сеанса требует блокировки ключа U.

Если индекс кластеризован , в этом случае тупиковая ситуация отсутствует. «// шаг 1» сохранит блокировку строки и ключа одновременно, поэтому проблем нет. Я понимаю, что блокировка строки также заблокирует индекс, поскольку конечный узел кластерного индекса - это данные строки.

Но почему некластеризованный индекс именно таким образом? Если во втором сеансе удерживается блокировка ключа U, то почему «шаг 1» первого сеанса не удерживает эту блокировку, поскольку они совпадают с оператором обновления.

--// first session
BEGIN TRAN
  update Book set name = name where id = 1 //step 1
  WaitFor Delay '00:00:20'
  update Book set name = 'trans' where id = 1 //step2
COMMIT

--// second session
BEGIN TRAN
--// this statement will keep both RID(U lock) and KEY(U lock) if first session did not use HOLDLOCK
  update Book set name = name where id = 1
COMMIT

Ответы [ 3 ]

11 голосов
/ 20 января 2010

Важным фактором здесь является то, что вы используете столбец в предложении where с некластеризованным индексом. Когда SQL Server обрабатывает обновление, оно выглядит примерно так:

  1. Поиск строк для обновления с использованием U-блокировок для затронутых данных
  2. Обновлять строки, принимая X-блокировки для измененных данных

После завершения оператора (по умолчанию READ COMMITTED изоляция) блокировки U снимаются, но блокировки X удерживаются до конца транзакции для поддержания изоляции.

В ситуации некластеризованного индекса SQL Server ищет индекс по идентификатору и использует его для поиска фактической строки. Блокировка срабатывает так:

  1. (Сессия 1, шаг 1) U-блокировка для значения ключа индекса для id = 1
  2. (Сессия 1, шаг 1) X-блокировка для RID для строки с id = 1
  3. (Сессия 1, шаг 1) U блокировка снята
  4. (Сессия 2) U блокировка на значение ключа индекса для id = 1
  5. (Сессия 2) Блокировка X заблокирована для RID для строки с id = 1
  6. (Сессия 1, шаг 2) Блокировка U заблокирована для значения ключа индекса для id = 1 - DEADLOCK

Однако, когда индекс является кластерным индексом, нет отдельного шага для преобразования ключа индекса в строку - значение кластеризованного индекса равно идентификатору строки. Следовательно, блокировка заканчивается так:

  1. (Сессия 1, шаг 1) U-блокировка для значения ключа индекса для id = 1
  2. (Сессия 1, шаг 1) U-блокировка обновлена ​​до X-блокировки
  3. (Сессия 2) Блокировка U заблокирована для значения ключа индекса для id = 1
  4. (Сессия 1, шаг 2) блокировка уже удерживается для значения ключа индекса для id = 1
  5. (Session 1, commit) блокировка снята
  6. (Сессия 2) U блокировка предоставлена ​​
  7. (Сессия 2) U-блокировка обновлена ​​до X-блокировки
  8. (Сессия 2) блокировка снята

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

0 голосов
/ 20 января 2010

Ваше первое обновление фактически ничего не меняет:

update Book set name = name where id = 1

Ваша команда, которая на самом деле изменяет ваш столбец, тогда в строке будет удерживаться эксклюзивная блокировка.

0 голосов
/ 20 января 2010

Эта ссылка содержит множество полезных советов: тупики SQL Server между выбором / обновлением или множественным выбором .

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

  1. Какой уровень изоляции транзакций вы используете?
  2. Разрешено ли наращивание блокировки (например, от строки к странице)?
  3. Есть ли индекс в столбце 'name'?
...