Отсюда можно сделать вывод, что данные обновляются
непосредственно, а менеджер блокировок следует какому-то специальному
алгоритму, проверяющему лог транзакций (transaction log), при использовании
хинта readpast.
На будущее хочу заметить, что тип страницы (выделен)
может принимать несколько значений, из которых для нас важны два: 1 – для
страниц данных, 2 – для страниц индексов.
Давайте продвигаться дальше. Так как в данный момент у
нас заблокирована одна строка (вторая), попытаемся во второй сессии выполнить
такой запрос:
--select * from test with (readpast)
select * from test where i = 1
|
Ура! Наконец-то она заблокировалась! :) Это произошло
потому, что в отсутствии индекса на таблице оптимизатор выполняет полное
сканирование таблицы для поиска строк, удовлетворяющих заданному логическому
условию. При этом он неизбежно натыкается на заблокированную строчку, что
приводит к блокировке транзакции. Вот какие блокировки установлены на текущий
момент (сокращенно, таблица 15):
ObjId
|
IndId
|
Type
|
Resource
|
Mode
|
Status
|
1993058136
|
0
|
PAG
|
1:31
|
IS
|
GRANT
|
1993058136
|
0
|
PAG
|
1:31
|
IX
|
GRANT
|
1993058136
|
0
|
TAB
|
|
IS
|
GRANT
|
1993058136
|
0
|
TAB
|
|
IX
|
GRANT
|
1993058136
|
0
|
RID
|
1:31:01
|
X
|
GRANT
|
1993058136
|
0
|
RID
|
1:31:01
|
S
|
WAIT
|
1993058136
|
0
|
PAG
|
1:31
|
IS
|
GRANT
|
1993058136
|
0
|
PAG
|
1:31
|
IX
|
GRANT
|
1993058136
|
0
|
TAB
|
|
IS
|
GRANT
|
1993058136
|
0
|
TAB
|
|
IX
|
GRANT
|
Мы видим, что на первую строку наложена монопольная
блокировка и какой-то процесс (а точнее, наша вторая сессия) хочет получить на
эту же строку разделяемую блокировку. Чтобы решить эту проблему, нужен индекс.
Но прежде нужно разобраться с текущей ситуацией.
Предположим, вы не имеете доступа к сессиям 54 и 55, которые в данный момент конкурируют в получении доступа к ресурсам, и не можете
выполнить команды фиксации транзакции или отката. Единственным возможным
выходом из сложившейся ситуации является убиение одного из процессов. Для
получения подробной информации о процессе можно воспользоваться хранимой
процедурой sp_who и ее недокументированным аналогом sp_who2. Последняя
возвращает значительно больше информации, однако результаты выполнения обеих
содержат колонку cmd, в которой хранится тип выполняющейся команды, если
процесс заблокирован, или AWAITING COMMAND – если все нормально.
Для получения более подробной информации о
выполняющейся команде можно использовать следующую команду:
Эта команда возвращает такой результат:
EventType Parameters EventInfo
-------------- ----------
-----------------------------------------------
Language Event 0 --SET IMPLICIT_TRANSACTIONS on
--select
* from test where i = 1
|
Эта команда может использоваться для просмотра первых
255 символов команд для заданной сессии.
После того, как выяснится жертва, можно вызывать
команду KILL.
Однако все это не про нас, вернемся к первой сессии (с
номером 54) и отменим транзакцию:
--SET IMPLICIT_TRANSACTIONS on
--update test set n = 'other' where i =
2
rollback
|
Вторая сессия моментально разблокируется, после чего в
ней также нужно откатить транзакцию:
--SET IMPLICIT_TRANSACTIONS on
--select
* from test with (readpast)
--select * from test where i = 1
rollback
|
Давайте создадим некластерный индекс и выполним те же
самые действия. Откройте новую сессию и выполните следующую команду: