SQL Server里的闩锁耦合(Latch Coupling)

几年前,我写了篇关于闩锁和为什么SQL Server需要它们的文章。在今天的文章里,我想进一步谈下非缓存区闩锁(Non-Buffer Latches),还有在索引查找操作期间,SQL Server如何使用它们。在这里你会学到称为闩锁耦合(Latch Coupling)的概念。

索引查找操作(Index Seek Operations)

正如你知道的,SQL Server使用扫描(Scan)和查找(Seek)操作在索引(聚集和非聚集索引)里访问数据。这里的查找操作使用B树的导航结构在叶子节点查找特定的记录。下图展示了这个概念。

在这个例子里,SQL Server读取索引根页,在层级下的索引页,最后在叶子级别读取数据页。每次SQL Server在缓存池里访问这个页,这个页需要获得共享闩锁(Shared Latch)。共享闩锁是至关重要的,因为在内存里,它让当下处理的页只读:

  • 每个排它闩锁(Exclusive Latch)和共享闩锁不兼容。

因此请求一个排它闩锁会阻塞,SQL Server会提示你有个PAGELATCH_EX等待类型。

现在我们来看下在查找操作期间,在索引页上,SQL Server如何获取和释放这些闩锁。下列代码展示了对于一个特定的会话ID,可以捕获latch_acquired和latch_released事件的扩展事件会话(根据实际情况修改会话ID)。

CREATE EVENT SESSION LatchTracking ON SERVER 
ADD EVENT sqlserver.latch_acquired
(
    ACTION
    (
        sqlserver.database_id,
        sqlserver.session_id,
        sqlserver.sql_text
    )
    WHERE
    (
        [package0].[equal_uint64]([sqlserver].[session_id],(54)) AND [class]=(28))
    ),
ADD EVENT sqlserver.latch_released
(
    ACTION
    (
        sqlserver.database_id,sqlserver.session_id,sqlserver.sql_text
    )
    WHERE
    (
        [package0].[equal_uint64]([sqlserver].[session_id],(54)) AND [class]=(28))
    )
ADD TARGET package0.event_file
(
    SET filename=N'c:\temp\LatchTracking.xel'
)
WITH
(
    MAX_MEMORY = 4096 KB,
    EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS,
    MAX_DISPATCH_LATENCY = 30 SECONDS,
    MAX_EVENT_SIZE = 0 KB,
    MEMORY_PARTITION_MODE = NONE,
    TRACK_CAUSALITY = OFF,
    STARTUP_STATE = OFF
)
GO

 注意:在“class”属性上的筛选谓语限制了缓存闩锁。它们的内部ID是28。是的,扩展事件是自表述的……

下一步,我现在用一个表来进行聚集索引查找操作,在那个表上我已经创建了聚集索引,在导航层级里包含三层(包含叶子层)。

闩锁耦合实战(Latch Coupling in Action)

扩展事件会话向你展示了,在聚集索引查找操作期间,对于整个会话需要那个缓存闩锁(只要你修改的会话ID是正确的……)。当你查看输出时,你会看到我们已经捕获了6个事件:3个latch_acquired事件, 和3个latch_released事件。

 

但更有意思的事是SQL Server获得和释放这些闩锁的顺序。一般你期望SQL Server在页上获得闩锁,并最后释放这个闩锁。但事实并非如此!

我们来详细看下。首先SQL 在索引根页(975号页)上获得了一个共享闩锁。在SQL Server处理那个页后,聚集索引查找操作在接下来的层级里,继续读取请求的页,并在它上面获取闩锁(257号页)。

注意在索引根页上获得的闩锁还没有释放,它还保持获取!

当在接下来的索引页上成功获取闩锁后,在索引根页上的闩锁才会释放。这个方法称为闩锁耦合(Latch Coupling)。这个必须的,因为SQL Server在B树结构里,跟随从一个页到另一个页的指针。

在页处理期间,这个指针必须保持稳定。例如,在此期间不允许被另一个工作者线程(例如分页操作)将此指针无效。因此SQL Server在(单线程)索引查找操作期间,同时把持2个闩锁。下面这个图片很好的演示了这个重要概念。

 

当SQL Server在下层的页(页号257)上成功获取共享闩锁后,在索引根页(页号975)上的共享闩锁被释放。当SQL Server在中间层处理了这个页后,SQL Server在叶子层级的数据页(页号256)上获得共享闩锁,然后并在上层的页(页号257)上释放共享闩锁。当这个页成功处理后,最后在页号265上的共享闩锁也被释放。

小结

在这篇文章里我向你展示了在索引查找操作中,通过所谓的闩锁耦合概念,SQL Server如何获取和释放闩锁。一个常见的误解,在查找操作期间,SQL Server只在特定的页上获取闩锁。如你在今天的文章所见,这个并不真的正确。

感谢您的关注!

原文链接

http://www.sqlpassion.at/archive/2016/10/24/latch-coupling-in-sql-server/

posted @ 2016-11-02 16:28  Woodytu  阅读(1204)  评论(0编辑  收藏  举报