在回滚期间,锁升级是否反转

Mic*_*een 5 sql-server sql-server-2008-r2 rollback locking

通常,SQL Server 会在更新期间获取锁。它还支持锁升级

锁升级是将许多细粒度锁转换为较少粗粒度锁的过程,减少系统开销的同时增加并发争用的概率。

在事务 ROLLBACK 期间,SQL Server 是否会降级锁,放回细粒度锁?

Mic*_*een 10

SQL Server 不会降级锁定。

我使用具有 100,000 行的“ Numbers ”表进行了调查。根据经验,更新 5,000 行会在sys.dm_tran_locks. 再更新 10,000 行导致升级为单个表锁。这始终是可重现的。为了最小化所涉及的对象,该表是一个没有索引的堆。

我使用扩展事件跟踪来捕获lock_acquiredlock_released事件。为了便于分析,我在 UPDATE 和 ROLLBACK 阶段使用了单独的跟踪。

使用了两个会话(SSMS 窗口)——一个用于 DML 语句,另一个用于跟踪 DDL。我无法使用单个会话,因为我想在事务打开时停止和启动跟踪,这是不允许的。隔离级别始终为 READCOMITTED。

结果select @@VERSION

Microsoft SQL Server 2008 R2 (SP2) - 10.50.4042.0 (Intel X86) 2015 年 3 月 26 日 21:49:16 版权所有 (c) Microsoft Corporation Enterprise Edition on Windows NT 6.1(Build 7601:Service Pack 1)

采取的步骤是

  1. 创建并启动获取跟踪。

    CREATE EVENT SESSION DuringUpdate
    ON SERVER
    ADD EVENT sqlserver.lock_acquired,
    ADD EVENT sqlserver.lock_released
    ADD TARGET package0.asynchronous_file_target
        (SET filename = 'c:\temp\LockRollback\DuringUpdate.xel',
         metadatafile = 'c:\temp\LockRollback\DuringUpdate.xem')
    WITH(EVENT_RETENTION_MODE = NO_EVENT_LOSS,
         MAX_DISPATCH_LATENCY = 1 SECONDS
        );
    
    
    ALTER EVENT SESSION DuringUpdate
    ON SERVER
    STATE = start;
    
    Run Code Online (Sandbox Code Playgroud)
  2. 开始一个事务并更新 5000 行。

    begin transaction;
    
    update dbo.Numbers
    set Number = Number
    where Number between 1 and 5000;
    
    Run Code Online (Sandbox Code Playgroud)
  3. 再更新 10000 行。

    update dbo.Numbers
    set Number = Number
    where Number between 5001 and 15000;
    
    Run Code Online (Sandbox Code Playgroud)
  4. 停止获取跟踪。

    ALTER EVENT SESSION DuringUpdate
    ON SERVER
    STATE = stop;
    
    Run Code Online (Sandbox Code Playgroud)
  5. 通过查询sys.dm_tran_locks(2 - DB, table)验证是否有锁。

  6. 创建并启动发布跟踪。

    CREATE EVENT SESSION DuringRollback
    ON SERVER
    ADD EVENT sqlserver.lock_acquired,
    ADD EVENT sqlserver.lock_released
    ADD TARGET package0.asynchronous_file_target
        (SET filename = 'c:\temp\LockRollback\DuringRollback.xel', 
             metadatafile = 'c:\temp\LockRollback\DuringRollback.xem')
    WITH(EVENT_RETENTION_MODE = NO_EVENT_LOSS,
         MAX_DISPATCH_LATENCY = 1 SECONDS
        );
    
    
    ALTER EVENT SESSION DuringRollback
    ON SERVER
    STATE = start;
    
    Run Code Online (Sandbox Code Playgroud)
  7. 回滚事务。

    rollback;
    
    Run Code Online (Sandbox Code Playgroud)
  8. 停止发布跟踪。

    ALTER EVENT SESSION DuringRollback
    ON SERVER
    STATE = stop;
    
    Run Code Online (Sandbox Code Playgroud)
  9. 整理

    DROP EVENT SESSION DuringUpdate
    ON SERVER;
    
    DROP EVENT SESSION DuringRollback
    ON SERVER;
    
    Run Code Online (Sandbox Code Playgroud)

如果发生锁降级,我希望在两个跟踪文件中完全对称 - 在 UPDATE 和升级期间获取的每个锁在 ROLLBACK 期间都会有相应的释放。我观察到的:

SELECT 
    COUNT(*) as DuringUpdate
FROM sys.fn_xe_file_target_read_file(
    'c:\temp\LockRollback\DuringUpdate*.xel', 
    'c:\temp\LockRollback\DuringUpdate*.xem', null, null);

SELECT 
    COUNT(*) as DuringRollback
FROM sys.fn_xe_file_target_read_file(
    'c:\temp\LockRollback\DuringRollback*.xel', 
    'c:\temp\LockRollback\DuringRollback*.xem', null, null);
Run Code Online (Sandbox Code Playgroud)

分别为 383,889 行和 166 行。检查回滚文件的事件 XML,只有一个与 RID、PAGE 或 OBJECTS 相关的事件 - 表上的 X 锁的释放。我由此得出结论,在回滚期间锁没有降级。

这与回滚到命名保存点的文档相呼应:

指定 savepoint_name 的 ROLLBACK TRANSACTION 语句会释放在保存点之外获取的任何锁,升级和转换除外。这些锁不会被释放,也不会转换回它们之前的锁模式。