You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何单独执行SELECT查询时无法看到SQL Server共享锁?

SQL Server SELECT查询未观测到共享锁的原因

问题背景

测试表结构

CREATE TABLE [dbo].[Events]
(
    [sequenceNumber] [bigint] IDENTITY(1,1) NOT NULL,
    [event] [varbinary](max) NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

测试现象

  1. 单独执行读事务无S锁
    执行以下事务后,通过sys.dm_tran_locks和sp_lock均未检测到共享锁(S锁):
BEGIN TRANSACTION;

SELECT TOP (1) 
    sequenceNumber, event 
FROM
    Events 
ORDER BY
    sequenceNumber DESC;
  1. 并发读写时出现S锁
    先执行未提交的写入事务:
BEGIN TRANSACTION;

INSERT INTO Events (event) VALUES (CONVERT(varbinary, 'Two'))

再执行读事务:

BEGIN TRANSACTION;

SELECT TOP (1) 
    sequenceNumber, event 
FROM
    Events 
ORDER BY
    sequenceNumber DESC;

此时通过sp_lock可观测到排他锁(X锁)与共享锁(S锁)。

疑问

为何单独执行SELECT时看不到S锁?SQL Server是否在该场景下采用无锁读取?

核心原因

这是SQL Server默认读取提交(Read Committed)隔离级别的特性导致的:

  1. 无并发时的锁行为
    在读取提交级别下,SQL Server默认会采用两种优化逻辑:
  • 若数据库开启了读取提交快照隔离(RCSI),SELECT会读取数据的行版本,完全不需要获取S锁;
  • 即使未开启RCSI,SQL Server也会采用“语句级锁释放”:SELECT执行时会临时获取S锁,但语句执行完毕后立即释放锁——由于锁持有时间极短,你几乎无法通过sys.dm_tran_locks或sp_lock捕获到。
  1. 并发场景下的锁行为
    当存在未提交的写入事务持有X锁时,若SELECT需要读取被锁定的行(比如你取最新插入的行):
  • 若未开启RCSI,SQL Server无法通过行版本获取一致数据,会主动请求S锁并等待X锁释放,此时锁持有时间足够长,能被工具检测到;
  • 若开启了RCSI,SELECT会直接读取该行的快照版本,不会请求S锁,也就看不到S锁。

验证方式

若想强制观测到S锁,可通过以下两种方式:

  • 修改事务隔离级别为可重复读:
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRANSACTION;
SELECT TOP (1) sequenceNumber, event FROM Events ORDER BY sequenceNumber DESC;
-- 此时查询sys.dm_tran_locks即可看到S锁
  • 关闭数据库的读取提交快照隔离:
ALTER DATABASE YourDatabaseName SET READ_COMMITTED_SNAPSHOT OFF;
-- 之后在默认隔离级别下执行读事务,就能观测到S锁

内容的提问来源于stack exchange,提问作者Pragun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 12:57:03