SQL Server, вводящий в заблуждение XLOCK и оптимизация

Из недавнего тестирования и чтения, которое я сделал, кажется, что часть имени "X" (исключительная) в XLOCK вводит в заблуждение. Фактически, он не блокирует больше, чем UPDLOCK. Если бы он был эксклюзивным, это предотвратило бы внешние SELECT, чего не делает.

Я не вижу ни чтения, ни тестирования и разницы между ними.

Единственный раз, когда XLOCK создает монопольную блокировку, - это когда используется с TABLOCK. Мой первый вопрос: "Почему только на такой степени детализации?"

Кроме того, я наткнулся на блог, в котором говорится следующее:

Однако следите за подсказкой XLOCK. SQL Server будет игнорировать подсказку XLOCK! Есть оптимизация, при которой SQL Server проверяет, изменились ли данные с момента самой старой открытой транзакции. Если нет, то xlock игнорируется. Это делает подсказки xlock в основном бесполезными, и их следует избегать.

Кто-нибудь сталкивался с этим явлением?

Судя по тому, что я вижу, кажется, что эту подсказку следует игнорировать.


person IamIC    schedule 05.01.2011    source источник
comment
Подробнее об оптимизации Tibor здесь говорилось. Я видел, как это происходило сейчас в моем собственном тестировании - его формулировка заставила меня подумать, что он говорит, что подсказка блокировки X будет проигнорирована. Это не тот случай. X замки сняты. Конкретная оптимизация заключается в том, чтобы не снимать S блокировки, чтобы не было конфликта. Объяснено гораздо лучше в связанной статье.   -  person Martin Smith    schedule 07.01.2011


Ответы (3)


Эксклюзивность X замков по сравнению с U замками

В матрице совместимости блокировок ниже можно увидеть, что X блокировка совместим только со стабильностью схемы и типами блокировки Insert Range-Null. U совместим со следующими дополнительными типами общих блокировок _5 _ / _ 6 _ / _ 7 _ / _ 8 _ / _ 9_

http://i.msdn.microsoft.com/ms186396.LockConflictTable(en-us,SQL.105).gif

Степень детализации X блокировок

Их выносят штрафом на всех уровнях. Трассировка скрипта и профилировщика ниже демонстрирует их успешное извлечение на уровне строки.

CREATE TABLE test_table (id int identity(1,1) primary key, col char(40))

INSERT INTO test_table
SELECT NEWID() FROM sys.objects

select * from test_table with (rowlock,XLOCK) where id=10

Trace

Но строки все еще можно читать!

Оказывается, на read committed уровне изоляции SQL Server не всегда снимает S блокировок, этот шаг будет пропущен, если без них нет риска прочитать незафиксированные данные. Это означает, что нет никакой гарантии, что конфликт блокировок когда-либо произойдет.

Однако, если начальный выбор with (paglock,XLOCK), то этот остановит транзакцию чтения, поскольку блокировка X на странице заблокирует IS блокировку страницы, которая всегда будет нужна читателю. Это, конечно, повлияет на параллелизм.

Прочие предостережения

Даже если вы заблокируете строку / страницу, это не означает, что вы заблокируете весь доступ к этой строке в таблице. Блокировка строки в кластеризованном индексе не помешает запросам читать данные из соответствующей строки в покрывающем некластеризованном индексе.

person Martin Smith    schedule 05.01.2011
comment
как насчет Insert Range-Null? - person KM.; 06.01.2011
comment
@KM - Хорошее место. Просто просмотрел и не заметил этого. Не уверен, для чего именно это. - person Martin Smith; 06.01.2011
comment
Ты знаешь свое дело @Martin. Это действительно помогло бы, если бы MS использовала более точное именование и имела не вводящую в заблуждение документацию. - person IamIC; 06.01.2011
comment
@IanC, скорее всего, подсказка XLOCK восходит к Sybase: en.wikipedia.org/ wiki / Microsoft_SQL_Server # Genesis - person KM.; 06.01.2011
comment
+1, чтобы отменить предыдущую ошибку :-) @KM Это возможно. Было бы интересно спросить кого-нибудь, кто работал над Sybase (не знаю, делал ли вы это). - person IamIC; 06.01.2011
comment
@Martin Мне нужно провести небольшое исследование, чтобы полностью понять этот след :-). Кстати, правильно ли я разместил фрагмент сообщения в блоге? - person IamIC; 06.01.2011
comment
Раньше я работал с Sybase, но могу сказать только it is most likely that..., потому что с трудом могу вспомнить основные различия между последними версиями, не говоря уже о старых вещах ... - person KM.; 06.01.2011
comment
@IanC - Я как раз смотрел на это. Думаю, мне нужно перезапустить мой SQL Server, чтобы самая старая открытая транзакция определенно была моложе самого последнего изменения. - person Martin Smith; 06.01.2011
comment
@KM, ты заставляешь себя казаться намного старше, чем ты, вероятно;) - person IamIC; 06.01.2011
comment
@ Мартин, спасибо. Я предполагаю, что вердикт - использовать только UPDLOCK. Поскольку NOLOCK устарел, а XLOCK ведет себя не так, как ожидалось, это оставляет UPDLOCK в качестве единственного режима (при необходимости). - person IamIC; 06.01.2011
comment
@IanC - После перезапуска трассировка все еще показывала, что X блокировки были сняты, как ожидалось, поэтому я не уверен, является ли это ошибка, которая теперь исправлена, или я неправильно понял обстоятельства, которые должны были вызвать эту проблему . - person Martin Smith; 06.01.2011
comment
@ Мартин Верно. К сожалению, это выходит за рамки моих текущих знаний. Когда вы говорите об ошибке, которая теперь исправлена, ссылаетесь ли вы на утверждение в сообщении в блоге? - person IamIC; 06.01.2011
comment
@IanC - Да, оптимизация, о которой говорит Тибор, мне не показалась. После перезапуска в данных не было никаких изменений, поэтому бит, в котором он говорит, что SQL Server проверяет, изменились ли данные с момента самой старой открытой транзакции. Если нет, то в моем тесте xlock игнорируется. - person Martin Smith; 06.01.2011
comment
@ Мартин Хорошо. Учитывая все вышесказанное, я считаю, что можно с уверенностью заключить, что UPDLOCK следует использовать в пользу XLOCK, поскольку UPDLOCK интуитивно понятен, а XLOCK вводит в заблуждение. Вы согласны? - person IamIC; 06.01.2011
comment
Они делают разные вещи. UPDLOCK разрешит одновременное чтение XLOCK не будет. Какова цель использования подсказки блокировки? - person Martin Smith; 06.01.2011
comment
@Martin Итак, XLOCK предотвратит одновременное чтение того, что было обновлено, тогда как UPDLOCK разрешит чтение (исходной версии). Правильно? - person IamIC; 06.01.2011
comment
Нет. Блокировки снимаются с определенных ресурсов (строки, страницы, таблицы и т. Д.). Только совместимые блокировки могут быть сняты с одного и того же ресурса одновременно. Читателю через стандартный select без подсказок блокировки потребуется S блокировка (игнорируя изоляцию моментальных снимков). Это совместимо с блокировкой U, но не X. Таким образом, считыватель будет заблокирован до тех пор, пока блокировка X не будет снята. (В запросе в моем ответе, когда будет снята блокировка, зависит от того, будет ли уровень изоляции serializable или используется holdlock, если оба значения верны, он будет удерживаться до конца транзакции) - person Martin Smith; 06.01.2011
comment
@IanC - попробуйте добавить holdlock и поместить свой оператор в явную транзакцию. - person Martin Smith; 06.01.2011
comment
Я использую явные транзакции ... Иначе я бы ничего не тестировал :). Выбор с помощью (HOLDLOCK, ROWLOCK) не мешал внешнему считывателю читать строку. Однако то, что я сказал выше, по-прежнему остается верным, согласно моим тестам. - person IamIC; 06.01.2011
comment
Я знаю, что HOLDLOCK будет удерживать блокировку при обновлении до конца транзакции. - person IamIC; 06.01.2011
comment
Спасибо @Martin. Я перепроверил MSDN: XLOCK: указывает, что монопольные блокировки должны приниматься и удерживаться до завершения транзакции. Не уверен, какую ошибку я допустил раньше, но теперь все работает так, как ожидалось. Однако я все еще не могу понять разницу в поведении между XLOCK и UPDLOCK. Оба блокируют внешние считыватели до конца транзакции. - person IamIC; 06.01.2011
comment
Хорошо ... общий, эксклюзивный. Тупики. :) - person IamIC; 06.01.2011
comment
@Martin по умолчанию: READCOMMITTED. - person IamIC; 06.01.2011
comment
Я вижу из Google и блогов, что мой вопрос довольно частый. Ответы довольно противоречивые. Вы сказали: «Читателю через стандартный выбор без подсказок блокировки потребуется S-блокировка (игнорируя изоляцию моментальных снимков)». S = общий, правда? Какой замок вы имеете в виду конкретно? - person IamIC; 06.01.2011
comment
Ему потребуется блокировка S для строки / страницы / таблицы и (при условии, что предложение where соответствует одной строке и требуется блокировка строки) любых ресурсов, которые ему необходимо прочитать, чтобы добраться до строки. Поэтому, если ему нужно выполнить сканирование кластерного индекса, ему потребуется общая блокировка для всех страниц или строк, просканированных в пути. Они освобождаются, как только страница будет прочитана на уровне зафиксированного чтения. - person Martin Smith; 06.01.2011
comment
Я просмотрел свои заметки и нашел одно различие между XLOCK и UPDLOCK. Выбор с помощью XLOCK предотвратит внешнее обновление. Выбор с помощью UPDLOCK ничего не делает (очевидно). - person IamIC; 06.01.2011
comment
когда я пытаюсь это проверить, я не вижу, чтобы это работало. Я делаю следующее: открываю новое окно в SSMS, запускаю declare @ID int;begin transaction;select @id=id from MyTable WITH (rowlock,XLOCK, HOLDLOCK) where id=8716;print @id, теперь открываю другое окно в SSMS, запускаю select * from MyTable where id=8716 и получаю строку обратно. Что мне не хватает? - person KM.; 06.01.2011
comment
@KM - эээ, хорошо В. Не думайте, что вы что-то упускаете! Просто попробовал CREATE TABLE MyTable (id int primary key nonclustered,col2 int unique clustered) insert into MyTable values(8716,1) и ваш скрипт и получил тот же результат. Чтобы получить блокировку, я добавил WITH (INDEX=1) к обоим запросам и поднял транзакцию чтения до уровня изоляции REPEATABLE READ. sys.dm_tran_locks показывает, что он был заблокирован в ожидании блокировки строки S. При зафиксированном чтении Profiler показывает, что потребовалась только IS блокировка страницы - совместимая с блокировкой IX записывающей транзакции. Нет S блокировок ни для строки, ни для страницы, которая могла бы конфликтовать. - person Martin Smith; 07.01.2011
comment
-А вообще я думаю, что это оптимизация, о которой говорил Тибор. Вчера я смотрел не на ту вещь. Проблема не в том, что X замки не снимаются (это то, на что я смотрел). Оптимизация заключается в том, что транзакции чтения не всегда принимают S блокировки, поэтому гарантированного конфликта нет. sqlblog.com/ blogs / paul_white / archive / 11.01.2010 / - person Martin Smith; 07.01.2011
comment
@KM, @Martin нет необходимости включать HOLDLOCK с XLOCK. Последний автоматически сделает то же самое, что и первый. Я ранее цитировал об этом фрагмент MSDN. @Martin, конечно, я что-то упустил ... не сделал бы пост, если бы это было не так. - person IamIC; 07.01.2011
comment
@IanC - Да, я пришел к такому же выводу. Я думаю, что единственное различие, которое делает holdlock здесь, заключается в использовании исключительных блокировок диапазона, а не исключительных блокировок строк. - person Martin Smith; 07.01.2011
comment
Рад, что мой вопрос вызвал интерес и заставил задуматься :) - person IamIC; 07.01.2011
comment
@Martin на основе вашего комментария -А на самом деле ... тогда, похоже, моя исходная посылка о том, что лучше не использовать XLOCK - использовать UPDLOCK (если возможно), действительна. Вы согласны? - person IamIC; 07.01.2011
comment
@IanC - В самом деле - это было очень поучительно. Но теперь немного надоела тема блокировки! RE: Ваш последний вопрос, я думаю, это зависит от цели - какова цель использования подсказки блокировки? - person Martin Smith; 07.01.2011
comment
@Martin: да, это не самая захватывающая тема. Просто из-за вводящей в заблуждение документации. Как я уже сказал, форумы полны путаницы по этому поводу. Спасибо за вашу помощь. В качестве примечания, я удивлен, что за этот вопрос проголосовали 0. Конечно, я заметил, что в целом простые вопросы получают голоса. Хорошо, что мы здесь не за очками;) - person IamIC; 07.01.2011
comment
Я отказался от попыток понять капризы ТАК-голосования! - person Martin Smith; 07.01.2011
comment
@Martin, без сомнения, умное решение. - person IamIC; 07.01.2011

Это не предупреждение, это неправильное понимание того, что происходит в SELECT.

Простой SELECT не запрашивает общие блокировки, если страницы не содержат грязных данных и, следовательно, не блокируются XLOCK.

Чтобы вас заблокировал XLOCK, вам нужно работать с уровнем изоляции REPEATABLE READ. Это может быть вызвано двумя вещами:

  1. Изменение данных с помощью INSERT / UPDATE / DELETE. Обновляемая таблица не обязательно должна быть той, на которой включен XLOCK.
  2. Явный запрос REPEATABLE READ через уровень изоляции транзакции или табличную подсказку.
person James Dingle    schedule 08.08.2013
comment
Под №1, я предполагаю, что обновление может быть в связанной таблице? Какой сценарий вызовет это состояние? - person IamIC; 09.08.2013
comment
Любой стол. Он не обязательно должен быть связан внешним ключом, он может находиться в другой базе данных. Имеет значение тип инструкции (INSERT / UPDATE / DELETE). - person James Dingle; 25.09.2013

на основе комментариев в ответ @Martin, вот маленький скрипт (запустите разные части в разных окнах SSMS, чтобы проверить блокировку, предотвращающую SELECT:

--
--how to lock/block a SELECT as well as UPDATE/DELETE on a particular row
--

--drop table MyTable
--set up table to test with
CREATE TABLE MyTable (RowID int primary key clustered
                     ,RowValue int unique nonclustered not null) 

--populate test data
;WITH InsertData AS
(
    SELECT 4321 AS Number
    UNION ALL
    SELECT Number+1
        FROM InsertData
        WHERE Number<9322
)
INSERT MyTable
        (RowID,RowValue)
    SELECT
        Number, 98765-Number
        FROM InsertData
        ORDER BY Number
    OPTION (MAXRECURSION 5001)

-----------------------------------------------------------------------------
-- #1
--OPEN A NEW SSMS window and run this
--
--create lock to block select/insert/update/delete
DECLARE @ID int

BEGIN TRANSACTION

SELECT @ID=RowID FROM MyTable WITH (ROWLOCK, XLOCK, HOLDLOCK) WHERE RowID=6822
PRINT @ID

--COMMIT  --<<<only run the commit when you want to release the lock
          --<<<adfter opening the other new windows and running the SQL in them



-----------------------------------------------------------------------------
-- #2
--OPEN A NEW SSMS window and run this
--
--shows how a select must wait for the lock to be released
--I couldn't get SSMS to output any text while in the trnasaction, even though
--it was completing those commands (possibly buffering them?) so look at the
--time to see that the statements were executing, and the SELECT...WHERE RowID=6822
--was what was where this script is blocked and waiting
SELECT GETDATE() AS [start of run]
SELECT '1 of 2, will select row',* FROM MyTable Where RowID=6822
go
DECLARE @SumValue int
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT GETDATE() AS [before transaction, shouldn't be nuch difference]
BEGIN TRANSACTION
SELECT @SumValue=SUM(RowID) FROM MyTable WHERE ROWID<6000
SELECT GETDATE() AS [in transaction, shouldn't be much difference]
    , @SumValue AS SumValue
--everything to here will run immediately, but the select below will wait for the
-- lock to be removed
SELECT '2 of 2, will wait for lock',* FROM MyTable Where RowID=6822
SELECT GETDATE() AS [in transaction after lock was removed, should show a difference]
COMMIT


-----------------------------------------------------------------------------
-- #3
--OPEN A NEW SSMS window and run this
--
--show how an update must wait
UPDATE MyTable SET RowValue=1111 WHERE RowID=5000  --will run immediately
GO
UPDATE MyTable SET RowValue=1111 WHERE RowID=6822 --waits for the lock to be removed

-----------------------------------------------------------------------------
-- #4
--OPEN A NEW SSMS window and run this
--
--show how a delete must wait
DELETE MyTable WHERE RowID=5000 --will run immediately
go
DELETE MyTable WHERE RowID=6822  --waits for the lock to be removed
person KM.    schedule 07.01.2011