17

从我最近所做的一些测试和阅读来看,XLOCK 的“X”(专有)名称部分似乎具有误导性。事实上,它并没有锁定任何比 UPDLOCK 更多的东西。如果它是独占的,它将阻止外部 SELECT,但它不会。

我无法从阅读或测试中看到两者之间的差异。

XLOCK 唯一一次创建排他锁是在与 TABLOCK 一起使用时。我的第一个问题是“为什么只有这种粒度?”

此外,我遇到了一个博客,其中陈述了以下内容:

但是,请注意 XLOCK 提示。SQL Server 将有效地忽略 XLOCK 提示!有一个优化,SQL Server 检查自最旧的打开事务以来数据是否已更改。如果不是,则忽略 xlock。这使得 xlock 提示基本上没有用,应该避免。

有没有人遇到过这种现象?

根据我所看到的,似乎这个提示应该被忽略。

4

3 回答 3

20

X锁与U锁的排他性

在下面的锁兼容性矩阵中可以看出,X锁只兼容模式稳定性和插入范围-空锁类型。U兼容以下附加共享锁类型S////ISRS-SRI-SRX-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

痕迹

但是仍然可以读取行!

事实证明,在read committed隔离级别,SQL Server 不会总是取出S锁,如果没有读取未提交数据的风险,它会跳过这一步。这意味着不能保证会发生锁冲突。

但是,如果初始选择是,with (paglock,XLOCK)那么这停止读取事务,因为X页面上的锁将阻止IS读取器始终需要的页面锁。这当然会对并发性产生影响。

其他注意事项

即使您锁定了行/页,这并不意味着您阻止了对表中该行的所有访问。对聚集索引中的行进行锁定不会阻止查询从覆盖的非聚集索引中的相应行读取数据。

于 2011-01-05T21:27:30.263 回答
3

这不是警告,这是对 SELECT 中发生的事情的误解。

如果页面不包含脏数据,则单纯的 SELECT 不会请求共享锁,因此不会被 XLOCK 阻塞。

要被 XLOCK 阻塞,你需要运行在 REPEATABLE READ 隔离级别。有两件事可以触发:

  1. 通过 INSERT/UPDATE/DELETE 修改数据。更新的表不必是 XLOCK 所在的表。
  2. 通过事务隔离级别或表提示明确要求 REPEATABLE READ。
于 2013-08-08T19:34:25.633 回答
2

根据@Martin's answer中的评论,这里有一个小脚本(在不同的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
于 2011-01-07T15:20:26.487 回答