从我最近的一些测试和阅读中看来,似乎XLOCK的“X”(独家)名称部分具有误导性。它实际上不会锁定UPDLOCK。如果它是独占的,它将阻止外部SELECT,它不会。
我无法从阅读或测试中看到两者之间的差异。
XLOCK创建独占锁的唯一时间是与TABLOCK一起使用时。 我的第一个问题是“为什么只有这种粒度?”
此外,我遇到了一个blog,其中说明了以下内容:
但请注意XLOCK提示。 SQL Server将有效地忽略XLOCK提示! SQL Server检查自最早的打开事务以来数据是否已更改,这是一种优化。如果不是,则忽略xlock。这使得xlock提示基本上没用,应该避免使用。
有没有人遇到这种现象?
根据我所看到的情况,似乎应忽略此提示。
答案 0 :(得分:19)
X
锁定与U
锁在lock compatibility matrix below中可以看出X
锁只与架构稳定性和插入范围 - 空锁定类型兼容。 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
事实证明,在read committed
隔离级别,SQL Server并不总是会取出S
个锁,it will skip this step if there is no risk of reading uncommitted data without them。这意味着无法保证发生锁定冲突。
但是,如果初始选择为with (paglock,XLOCK)
,则此将停止读取事务,因为页面上的X
锁定将阻止IS
页面锁定读者总是需要它。这当然会对并发性产生影响。
即使您锁定行/页面,这也不意味着您阻止对表中该行的所有访问。对聚簇索引中的行执行锁定不会阻止查询从覆盖的非聚簇索引中的相应行读取数据。
答案 1 :(得分:3)
这不是一个警告,这是对SELECT中发生的事情的误解。
如果页面不包含脏数据,仅仅SELECT不会要求共享锁定,因此不会被XLOCK阻止。
要被XLOCK阻止,您需要以REPEATABLE READ隔离级别运行。有两件事可以触发:
答案 2 :(得分: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