我有下表:
CREATE TABLE [dbo].[table1](
[id] [int] IDENTITY(1,1) NOT NULL,
[name] [nvarchar](50) NULL,
CONSTRAINT [PK_table1] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
我正在学习SQL锁是如何工作的,我正在尝试测试我想要锁定行被读取和更新的情况。这个任务的一些灵感来自于这个article,而这里是我试图解决的original problem。
当我运行这个T-SQL时:
BEGIN TRANSACTION
SELECT * FROM dbo.table1 WITH (UPDLOCK, HOLDLOCK)
WAITFOR DELAY '00:00:15'
COMMIT TRANSACTION
我希望在表上放置一个独占锁,特别是对于行(如果我在主键上有一个WHERE语句)
但运行此查询,我可以看到GRANTed LOCK用于请求模式IX。
SELECT * FROM sys.dm_tran_locks WHERE resource_database_id = DB_ID() AND resource_associated_entity_id = OBJECT_ID(N'dbo.table1');
此外,在单独的SSMS窗口中,我可以在事务运行时完全查询表。
为什么MSSQL不尊重锁定提示?
(SQL Server 2016)
编辑1
有关这些锁如何工作的任何信息都很受欢迎,但是,现在的问题是SQL Server似乎没有强制执行我指定的锁。我的预感是这与行版本控制或相关的东西有关。
编辑2
我创建了这个Github gist。它需要.NET和外部库Dapper才能运行(可通过Nuget包获得)。
这是我注意到的有趣事情:
table1
的查询,也可以针对UPDLOCK, HOLDLOCK
运行SELECT语句。以下是该Gist的控制台输出:
运行锁定SELECT开始 - 00:00:00.0165118
运行非锁定SELECT开始 - 00:00:02.0155787
运行非锁定SELECT完成 - 00:00:02.0222536
运行INSERT开始 - 00:00:04.0156334
运行UPDATE ALL开始 - 00:00:06.0259382
运行UPDATE EXISTING开始 - 00:00:08.0216868
运行更新不存在开始 - 00:00:10.0236223
运行更新不存在完成 - 00:00:10.0268826
运行锁定SELECT完成 - 00:00:31.3204120
运行INSERT完成 - 00:00:31.3209670
运行UPDATE ALL已完成 - 00:00:31.3213625
运行UPDATE EXISTING完成 - 00:00:31.3219371
答案 0 :(得分:3)
我正在尝试测试我想要锁定行的情况 正在阅读和更新
如果要锁定读取和更新行,则需要独占锁定,但UPDLOCK
锁定提示请求更新锁定,而不是独占锁定。查询应为:
SELECT * FROM table1 WITH (XLOCK, HOLDLOCK, ROWLOCK)
WHERE Id = <some id>
此外,在READ COMMITTED SNAPSHOT
和SNAPSHOT
隔离级别下,SELECT
语句不会请求共享锁,只会请求架构稳定性锁。因此,SELECT
语句可以读取行,尽管存在独占锁。令人惊讶的是,在READ COMMITTED隔离级别下,SELECT语句可能不会请求行级共享锁。您需要在SELECT
语句中添加查询提示,以防止它读取锁定的行:
SELECT * FROM dbo.Table1 WITH (REPEATABLEREAD)
WHERE id = <some id>
使用REPEATABLEREAD
锁定提示时,SELECT
语句将请求共享锁并在事务期间保留它们,因此它不会读取独占锁定的行。请注意,使用READCOMMITTEDLOCK
是不够的,因为在某些情况下SQL Server可能不会请求共享锁,如this博文中所述。
在默认隔离级别READ COMMITTED
下,如果没有锁定提示,SELECT
语句会为其读取的每一行请求共享锁,并且在读取该行后立即释放这些锁。但是,如果使用WITH (HOLDLOCK)
,则会保留共享锁,直到事务结束。考虑到锁兼容性表,在SELECT
下运行的READ COMMITTED
语句可以读取任何未被锁定的行(IX,SIX,X锁)。 INSERT
,UPDATE
和DELETE
语句或带有SELECT
提示的XLOCK
语句请求独占锁定。
我希望在桌子上放置一个独占锁,并且 特别是对于行(如果我在主要上有一个WHERE语句 键)
我需要了解为什么SQL Server没有重新锁定锁定 赋予它的指令。 (即为什么不是专属锁定 表格,还是那行?)
UPDLOCK
提示不请求独占锁,它请求更新锁。此外,可以在除行本身之外的其他资源上授予锁定,可以在表,数据页,索引页和索引键上授予锁。 SQL Server可以锁定的资源类型的完整列表是:DATABASE, FILE, OBJECT, PAGE, KEY, EXTENT, RID, APPLICATION, METADATA, HOBT, and ALLOCATION_UNIT
。指定ROWLOCK
提示时,SQL Server将锁定行,而不是页面,范围或表,SQL Server将锁定的实际资源是RID
和KEY
的
答案 1 :(得分:0)
从本质上讲 - 您总是可以阅读 UNLESS ,要求使用相同的锁定类型(或更严格的限制)。但是,如果您想要UPDATE
或DELETE
,那么您将被屏蔽。但正如我所说,上面的链接解释得非常好。
答案 2 :(得分:0)
你的回答是正确的:
https://docs.microsoft.com/en-us/sql/t-sql/queries/hints-transact-sql-table
锁提示获取行级锁的ROWLOCK,UPDLOCK和XLOCK可能会锁定索引键而不是实际数据行。例如,如果表具有非聚簇索引,并且使用锁定提示的SELECT语句由覆盖索引处理,则会在覆盖索引中的索引键上获取锁定,而不是在基表中的数据行上获取锁定。 / p>
这就是为什么你得到索引锁(IX)而不是表行锁。
这解释了为什么你可以在运行第一个查询时阅读:
http://aboutsqlserver.com/2011/04/14/locking-in-microsoft-sql-server-part-1-lock-types/
更新锁(U)。这些锁是共享锁和独占锁之间的混合。 SQL Server在搜索需要修改的行时将它们与数据修改语句一起使用。例如,如果发出如下语句:“update MyTable set Column1 = 0其中Column1为null”SQL Server在搜索Column1时为其处理的每一行获取更新锁定为null。找到符合条件的行时,SQL Server会将(U)锁定转换为(X)。
你的UPDLock是一个更新锁。请注意,更新锁在搜索时是SHARED,在执行实际更新时更改为EXCLUSIVE。由于您的查询是带有更新锁定提示的选择,因此锁定是SHARED锁定。这将允许其他查询也读取行。