SQL Server,误导性的 XLOCK &优化

发布于 2024-10-10 16:51:37 字数 543 浏览 7 评论 0原文

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

我无法从阅读或测试中看出两者之间的区别。

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

此外,我遇到了博客 声明以下内容:

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

有人遇到过这种现象吗?

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

From some recent testing and reading I've done, it seems the "X" (exclusive) name part of XLOCK is misleading. It in fact doesn't lock any more than UPDLOCK. If it were exclusive, it would prevent external SELECTs, which it doesn't.

I cannot see either from reading or from testing and difference between the two.

The only time XLOCK creates an exclusive lock is when used with TABLOCK. My first question is "why only at this granularity?"

Further, I came across a blog that states the following:

However, watch out for XLOCK hint. SQL Server will effectively ignore XLOCK hint! There's an optimization where SQL Server check whether the data has changed since the oldest open transaction. If not, then an xlock is ignored. This makes xlock hints basically useless and should be avoided.

Has anyone run across this phenomenon?

Based on what I'm seeing, it seems this hint should be ignored.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

一花一树开 2024-10-17 16:51:37

X 锁与 U 锁的排他性

可以看出,X锁仅兼容schema稳定性和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 锁的粒度

这些各级都被罚款。下面的脚本和探查器跟踪演示了它们已在行级别成功取出。

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锁,如果不存在读取未提交数据的风险,它将跳过此步骤。这意味着不能保证永远发生锁冲突。

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

其他注意事项

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

Exclusivity of X locks vs U locks

In the lock compatibility matrix below it can be seen that the X lock is only compatible with the schema stability and Insert Range-Null lock types. U is compatible with the following additional shared lock types S/IS/RS-S/RI-S/RX-S

lock compatibility matrix http://i.msdn.microsoft.com/ms186396.LockConflictTable(en-us,SQL.105).gif

Granularity of X locks

These are taken out fine at all levels. The script and profiler trace below demonstrates them being successfully taken out at row level.

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

But rows can still be read!

It turns out that at read committed isolation level SQL Server will not always take out S locks, it will skip this step if there is no risk of reading uncommitted data without them. This means that there is no guarantee of a lock conflict ever occurring.

However if the initial select is with (paglock,XLOCK) then this will stop the reading transaction as the X lock on the page will block the IS page lock that will always be needed by the reader. This will of course have an impact on concurrency.

Other Caveats

Even if you lock the row/page this does not mean that you block all accesses to that row in the table. A lock on a row in the clustered index will not prevent queries reading data from the corresponding row in a covering non clustered index.

迷离° 2024-10-17 16:51:37

这不是一个警告,而是对 SELECT 中发生的情况的误解。

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

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

  1. 通过 INSERT/UPDATE/DELETE 修改数据。更新的表不必是 XLOCK 所在的表。
  2. 通过事务隔离级别或表提示显式请求 REPEATABLE READ。

It's not a caveat, it's a misunderstanding on what happens in SELECT.

A mere SELECT does not asks for Shared locks if the pages do not contain dirty data, and thus is not blocked by XLOCK.

To be blocked by XLOCK, you need to run in REPEATABLE READ isolation level. Two things can trigger that:

  1. Modifying data, through INSERT/UPDATE/DELETE. The table updated does not have to be the one the XLOCK is on.
  2. Explicitly asking for REPEATABLE READ through transaction isolation level or table hint.
咋地 2024-10-17 16:51:37

根据@Martin的回答中的评论,这是小脚本(在不同的 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

based on the comments in @Martin's answer, here is a little script (run the different parts in different SSMS windows to test the lock preventing a 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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文