我有一个具有以下架构的表:
CREATE TABLE [dbo].[Events]
(
[sequenceNumber] [bigint] IDENTITY(1,1) NOT NULL,
[event] [varbinary](max) NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
为了调试一些与锁定相关的问题,我试图了解 SQL Server 的锁定机制并尝试在本地复制这些情况。
但是有一件事我无法测试,那就是 SELECT 查询在正在读取的行上获取的共享锁。为了测试这一点,我运行了以下 T-SQL:
BEGIN TRANSACTION;
SELECT TOP (1)
sequenceNumber, event
FROM
Events
ORDER BY
sequenceNumber DESC;
根据我的理解,这应该会导致行或表上的
S
锁定?但检查 sys.dm_tran_locks
和 sp_lock
后,没有看到锁。
另一方面,如果我尝试执行写入并并行运行读取命令,那么我可以观察到
X
和 S
锁。
-- Write query
BEGIN TRANSACTION;
INSERT INTO Events (event) VALUES (CONVERT(varbinary, 'Two'))
-- Read query run after above write
BEGIN TRANSACTION;
SELECT TOP (1)
sequenceNumber, event
FROM
Events
ORDER BY
sequenceNumber DESC;
在这种情况下
sp_lock
的结果:
为什么仅使用
SELECT
语句共享锁不可见?
在这些情况下,引擎是否在没有任何共享锁的情况下执行读取?
我认为您遇到了时间问题。阅读 docs 以获取可序列化的查询提示:
相当于HOLDLOCK。通过持有共享锁直到事务完成来使共享锁更具限制性,而不是在不再需要所需的表或数据页时立即释放共享锁,无论事务是否已完成。使用与在可串行化隔离级别运行的事务相同的语义来执行扫描。
(强调我的)也就是说,获取锁、读取数据以及释放锁的速度比您切换到另一个会话并运行的速度更快
sp_lock
。
为了进一步探索,我可以看到(至少)两个选择
serializable
提示(或隔离级别),以便 select 不会释放锁。其中,我认为 XE 路线更好,因为您没有改变并发语义,这可以改变锁的性质和持续时间。但可序列化的路由肯定更容易设置。