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

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

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

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

далее, я наткнулся на блог это гласит следующее:

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

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

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

3 ответов


эксклюзивность X locks vs U замки

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

матрица совместимости блокировок http://i.msdn.microsoft.com/ms186396.LockConflictTable (en-us, SQL.105).gif

гранулярность X locks

они вынимаются нормально на всех уровнях. Сценарий и трассировка профилировщика ниже демонстрируют, что они успешно извлекаются на уровне строк.

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 блокировка страницы, которая всегда будет необходима читателю. Это, конечно, повлияет на параллелизм.

другой Предостережения

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


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

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

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

  1. изменение данных через INSERT/UPDATE / DELETE. Таблица обновляется не должен быть один XLOCK это на.
  2. явно запрашивает повторяемый уровень изоляции транзакций или табличную подсказку.

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

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