zoukankan      html  css  js  c++  java
  • 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:	empLatchTracking.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/

  • 相关阅读:
    SSL延迟有多大?(转)
    Nginx/LVS/HAProxy负载均衡软件的优缺点详解(转)
    lvs、haproxy、nginx 负载均衡的比较分析(转)
    入木三分学网络第一篇--VRRP协议详解第一篇(转)
    从一个开发的角度看负载均衡和LVS(转)
    四层和七层负载均衡的区别(转)
    geo实现方案
    HTTP API 设计指南(转)
    app后端设计(0)--总目录(转)
    Django接收自定义http header(转)
  • 原文地址:https://www.cnblogs.com/woodytu/p/6022367.html
Copyright © 2011-2022 走看看