SQL Server, вводящий в заблуждение XLOCK & оптимизации - PullRequest
17 голосов
/ 06 января 2011

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

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

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

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

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

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

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

Ответы [ 3 ]

20 голосов
/ 06 января 2011

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

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

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

Другие предупреждения

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

3 голосов
/ 08 августа 2013

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

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

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

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

на основе комментариев в @ ответ Мартина , вот небольшой скрипт (запустите разные части в разных окнах 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...