在SQL Server数据库中,统计信息更新(UPDATE STATISTICS)会被其它会话阻塞吗?统计信息更新(UPDATE STATISTICS)会引起其它会话阻塞吗?在回答这两个问题前,我们必须搞清楚,统计信息更新这个操作期间会申请/持有那些锁。如果弄清楚了这些,那么我们就能很容易回答这两个问题了。如果要弄清楚统计信息更新会申请/持有那些锁,我们可以用SQL脚本或SQL Server Profiler工具来查询/定位相关的锁信息
SQL脚本方式
SELECT * FROM sys.dm_tran_locks
WHERE request_session_id=xxx; --xxx用具体的会话ID替换
SELECT resource_type ,
resource_subtype ,
resource_description ,
resource_associated_entity_id ,
request_mode ,
request_type ,
request_status ,
request_session_id
FROM sys.dm_tran_locks
WHERE request_session_id = xxx;
用SQL脚本的话,不太容易捕捉到统计信息更新(UPDATE STATISTICS)整个过程中申请的所有相关锁信息,而且小表的统计信息更新速度非常快(时间短到你来不及去查询相关锁信息,有些锁就已经申请成功,并释放了,可能统计信息更新都已经完成了),如果要实验的话,可能需要构造一个很大的表。这种方式还是有一些缺陷与不足。
SQL Server Profiler跟踪方式
使用SQL Server Profiler工具追踪更新统计信息期间会申请/持有哪一些锁。这种方式比较容易捕捉到整个过程中所有相关的锁申请与锁释放的详细信息,而且用SQL Server Profiler跟踪锁的申请与释放也非常方便。个人推荐使用这种方式。
下面我们打开一个会话窗口,找出会话ID(当前测试环境会话ID为53),然后使用SQL Server Profiler跟踪会话ID=53的锁的申请与释放(Lock:Acquired, Lock:Released),此处SQL Server Profiler的相关操作细节略过。然后在会话窗口执行下面语句
UPDATE STATISTICS TEST WITH FULLSCAN,ALL;
如下部分截图所示,我们可以看到在统计信息更新期间,数据库会在相关对象上请求架构稳定锁(Sch-S)、架构修改锁(Sch-M)、共享锁(S),排它锁(X),意向排他锁(IX),更新锁(U)等,整个过程会有较多的锁申请与锁释放。
从上面实验可以看出,在 SQL Server 中,更新统计信息可能会申请持有很多类型的锁,那么我来一项项分析,在分析之前,我们来看一下锁的兼容矩阵,如果对这方面知识有点模糊不清的,正好可以重温一下这方面的知识点:
注意:除了架构修改锁 (Sch-M)之外,架构稳定锁 (Sch-S) 与所有锁定模式都兼容。而Sch-M 锁与所有锁定模式都不兼容。
1. 共享锁(S)
从实验数据来看,共享锁都发生在统计信息元数据对象上。这些元数据对象,如下截图所示,分别为sysschobjs和sysobjvalues,当然还有OBJECT_ID=0 OBJECTID2=xxxx的数据页或数据行。
SELECT t.object_id AS ObjectID,
OBJECT_NAME(t.object_id) AS ObjectName,
SUM(u.total_pages) * 8 AS Total_Reserved_kb,
SUM(u.used_pages) * 8 AS Used_Space_kb,
u.type_desc AS TypeDesc,
MAX(p.rows) AS RowsCount
FROM sys.allocation_units AS u
JOIN sys.partitions AS p ON u.container_id = p.hobt_id
JOIN sys.objects AS t ON p.object_id = t.object_id
where u.allocation_unit_id=281474980642816
GROUP BY t.object_id,
OBJECT_NAME(t.object_id),
u.type_desc
ORDER BY Used_Space_kb DESC,
ObjectName;
注意:查询条件中用实际具体的OBJECTID2的值替换。
从锁的兼容性来分析的话,这时发生阻塞与被阻塞的可能性是存在的,统计信息更新期间,在申请共享锁时,某些操作在元数据对象上持有意向排他共享锁(SIX)、意向排它锁(IX)、排它锁(X),例如并发的会话跟新统计数据等操作,实际场景中,统计信息更新很少在发生申请共享锁时阻塞其它会话与被其它会话阻塞。
2. 架构稳定性锁(Sch-S)
当UPDATE STATISTICS时,SQL Server 会获取架构稳定锁(Sch-S)。这里不仅仅是统计信息更新涉及的相关对象还包括统计信息元数据对象,都会获取Sch-S锁,而对于架构稳定锁(Sch-S)有下面一些规则:
- 允许其他会话继续读取或修改数据(如 SELECT、INSERT、UPDATE)。
- 仅阻塞需要修改表结构的操作(如 ALTER TABLE、CREATE INDEX),因为这些操作需要架构修改锁(Sch-M),与架构稳定锁(Sch-S)不兼容。
此时,除非有并发的会话对表结构进行修改(DDL)或者并发会话在进行统计信息更新操作,此时刚好持有 Sch-M 锁,那么就可能会被阻塞。 我们会结合架构修改锁(Sch-M)构造测试案例。
3. 架构修改锁(Sch-M)
当更新统计信息时,SQL Server 会尝试获取统计信息元数据对象上的架构修改锁(Sch-M)。这种锁用于确保在更新统计信息的过程中,其他会话不会对统计信息进行修改。如果其他会话已经持有与 Sch-M 不兼容的锁(如架构稳定性锁 Sch-S),则更新统计信息的操作可能会被阻塞。
这些元数据对象,如下截图所示,分别为sysschobjs和sysobjvalues等对象。
那么我们简单构造一下统计信息更新被阻塞的案例,如下所示
--会话58中执行下面语句,模拟事务正在修改表结构(DDL),此时事务未提交/事务正在执行阶段
begin tran
alter table test add kk varchar(30);
--rollback;
--会话53中执行下面语句更新统计信息
UPDATE STATISTICS TEST WITH FULLSCAN,ALL;
在会话窗口监控阻塞情况,如下所示,对表进行DDL操作时,会阻塞统计信息的更新,此时更新统计信息的会话的等待类型为LCK_M_SCH_S,意味着会话53正在等待获取架构稳定锁(Sch-S), 其实反过来,更新统计信息也会阻塞一些会话对相关表进行DDL操作。此时对相关表进行DDL操作时。个人也构造了另外一个大表测试案例进行了验证。有兴趣也可以验证一下。此处略过。
4. 意向排它锁(IX)与排它锁(X)与更新锁(U)
在更新统计信息时,SQL Server 还可能会对相关表(例如,sysobjvalues)中的数据行或页获取行锁(X、U 等)或页锁(IX、IU 等)。这些锁用于确保在采样数据时,数据不会被其他事务修改。如果有并发的DDL或统计信息跟新的话,也有可能导致阻塞与被阻塞。但是实际生产环境中,这种可能性非常小。另外,在表TEST进行统计信息更新时,也会在TEST上有一个短暂的排它锁(X),它的子类型(Resource subtype)为UPDSTATS,根据官方文档^[1]^,只要子类型不同(不同的子类型彼此之间不会冲突),它是不会阻塞表上的DML操作的,除非另外一个会话也在更新统计信息。这种概率微乎其微。
此处附上官方文档的内容:
Represents a subtype of resource_type. Acquiring a subtype lock without holding a non-subtyped lock of the parent type is technically valid. Different subtypes do not conflict with each other or with the non-subtyped parent type. Not all resource types have subtypes.
结论总结
统计信息更新可能被其它会话阻塞,统计信息更新也有可能阻塞其它会话。当统计信息更新时,会获取统计信息元数据对象上的架构修改锁(Sch-M)。如果其他会话已经锁定了同一对象,或者需要在统计信息元数据对象上获取架构稳定性锁(Sch-S)来编译查询的会话,可能会被阻塞。但是这种场景比较少;另外不要同时做DDL(修改表结构、创建维护索引)和统计信息更新操作,不要并发的去做统计信息更新(很少有这种场景)。绝大部分场景下,是可以大胆地执行统计信息更新操作,它不会阻塞数据操作(DML),不用担心它阻塞了其它会话或被阻塞的。
参考资料 [1]