SELECT查询中的索引与意向共享锁(IS锁)问题
三种索引场景下SELECT查询的锁行为详解
嘿,针对你用SQL Profiler监控到的锁行为,我来拆解一下细节——你观察到的Object级IS锁 + Page级IS锁其实是SQL Server读操作的标准前置流程,不管查询基于聚集索引、非聚集索引还是完全无索引,这两步锁获取都是必然发生的,原因如下:
- Object级IS锁:这是表级的意向共享锁,作用是告诉其他会话“我打算读这个表的数据”,防止有人对表执行DDL(比如
ALTER TABLE)这类会修改对象结构的操作,属于锁层级机制里的顶层意向声明。 - Page级IS锁:这是数据页级的意向共享锁,意思是“我可能要读这个页里的行数据”,避免其他会话直接对整个数据页加排他锁(X锁),保证读操作的并行性。
接下来重点说三个场景的核心差异——也就是行级锁的施加情况:
1. 基于聚集索引列的查询
当查询条件命中聚集索引时,SQL Server会通过聚集索引直接定位到目标数据行(毕竟聚集索引本身就是数据行的存储结构),此时会在找到的目标行上施加S锁(共享锁)。如果是默认的读提交隔离级别,这个S锁会持有到查询结束;如果开启了快照隔离或读提交快照(RCSI),则会使用行版本,不会持有S锁。
2. 基于非聚集索引列的查询
这里要分两种子情况:
- 如果你的非聚集索引是覆盖索引(包含了
SELECT *需要的所有列),SQL Server直接从非聚集索引获取完整数据,只会在非聚集索引的键行上施加S锁,不需要回表访问聚集索引。 - 如果是非覆盖索引,SQL Server需要通过非聚集索引里的书签(RID或聚集索引键)回表查找完整数据,此时会先在非聚集索引的匹配行上加S锁,再在对应的聚集索引行(也就是实际数据行)上加S锁。
3. 完全无索引的列(全表扫描)
这种场景下SQL Server会执行全表扫描,遍历每个数据页,此时会对扫描过程中访问到的每一行都施加S锁(默认读提交隔离级别下)。当然,如果开启了快照隔离或RCSI,同样会用行版本替代S锁,避免锁竞争。
最后补充一点:意向锁(IS)本身不会阻塞其他意向锁或共享锁,只会阻塞排他锁(X),所以三个场景下的Object和Page级IS锁行为完全一致,差异主要体现在行级锁的范围和数量上。
内容的提问来源于stack exchange,提问作者John L.
相关产品推荐
相关产品推荐

