2011-01-05 1 views
16

Из недавних тестов и чтения, которые я сделал, кажется, что «X» (эксклюзивная) часть названия XLOCK вводит в заблуждение. Он фактически не блокирует больше, чем UPDLOCK. Если бы это было эксклюзивно, это предотвратило бы внешние SELECT, которых нет.SQL Server, вводящий в заблуждение XLOCK и оптимизация

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

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

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

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

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

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

+0

Подробнее об оптимизации Тибор [говорил здесь] (http://sqlblog.com/blogs/paul_white/archive/2010/11/01/read-committed-shared-locks-and-rollbacks. ASPX). Я видел, как это произошло сейчас в моем собственном тестировании. Его фразирование заставило меня подумать, что он говорил, что подсказка «Х» будет проигнорирована. Это не тот случай. Блокировка 'X' вынимается. Конкретная оптимизация заключается в том, чтобы не вынимать блокировки 'S', поэтому конфликт никогда не возникает. Объясняется намного лучше в связанной статье. –

ответ

18

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

В lock compatibility matrix below можно видеть, что X замок совместим только с устойчивостью схемы и Insert Range-Null типов замков. U совместим со следующими дополнительными общими типами замков S/IS/RS-S/RI-S/RX-S

lock compatibility matrix 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

Но ряды еще можно прочитать!

Оказывается, что на уровне изоляции SQL Server не всегда вынимает S замки, it will skip this step if there is no risk of reading uncommitted data without them. Это означает, что нет никакой гарантии, что конфликт блокировки будет когда-либо происходить.

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

предостережений

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

+0

как насчет Insert Range-Null? –

+0

@KM - Хорошее место. Просто просмотрел и не заметил этого. Не уверен, что это за точно. –

+1

Ты знаешь свои вещи @ Мартин. Это действительно помогло бы, если бы MS использовала более точное именование и имела не вводящую в заблуждение документацию. – IamIC

2

на основе комментариев в @Martin's answer, вот небольшой скрипт (запускать различные части в разных ВСС окна, чтобы проверить блокировку предупреждения ОТБОРНОЕ:

-- 
--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 
3

Это не один нюанс, это недоразумение на том, что происходит в SELECT.

простой SELECT, не просит разделяемые блокировки, если страницы не содержат грязные данные, и, таким образом, не блокируются XLock.

чтобы быть заблокированы XLock, вам нужно запустить в REPEATABLE Изолировать READ ионного уровня. Две вещи могут вызвать это:

  1. Изменение данных через INSERT/UPDATE/DELETE. Обновленная таблица не должна быть той, на которой установлен XLOCK.
  2. Явно запрашиваю REPEATABLE READ через уровень изоляции транзакции или подсказку таблицы.
+0

К # 1, я предполагаю, что обновление может быть связано со связанной таблицей? Какой сценарий может вызвать это условие? – IamIC

+0

Любая таблица. Он не должен быть связан внешним ключом, он может быть в другой базе данных. Это тип инструкции, который имеет значение (INSERT/UPDATE/DELETE). –