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/

时间: 2025-01-02 15:32:12

SQL Server里的闩锁耦合(Latch Coupling)的相关文章

SQL Server里的闩锁介绍

在今天的文章里我想谈下SQL Server使用的更高级的,轻量级的同步对象:闩锁(Latch).闩锁是SQL Server存储引擎使用轻量级同步对象,用来保护多线程访问内存内结构.文章的第1部分我会介绍SQL Server里为什么需要闩锁,在第2部分我会给你介绍各个闩锁类型,还有你如何能对它们进行故障排除. 为什么我们需要闩锁? 闩锁首次在SQL Server 7.0里引入,同时微软首次引入了行级别锁(row-level locking).对于行级别锁引入闩锁的概念是非常重要的,不然的话在内存中

SQL Server里的自旋锁介绍

在上一篇文章里我讨论了SQL Server里的闩锁.在文章的最后我给你简单介绍了下自旋锁(Spinlock).基于那个基础,今天我会继续讨论SQL Server中的自旋锁,还有给你展示下如何对它们进行故障排除. 为什么我们需要自旋锁? 在上篇文章我已经指出,用闩锁同步多个线程间数据结构访问,在每个共享数据结构前都放置一个闩锁没有意义的.闩锁与此紧密关联:当你不能获得闩锁(因为其他人已经有一个不兼容的闩锁拿到),查询就会强制等待,并进入挂起(SUSPENDED)状态.查询在挂起状态等待直到可以拿到

在SQL Server里为什么我们需要更新锁

今天我想讲解一个特别的问题,在我每次讲解SQL Server里的锁和阻塞(Locking & Blocking)都会碰到的问题:在SQL Server里,为什么我们需要更新锁?在我们讲解具体需要的原因前,首先我想给你介绍下当更新锁(Update(U)Lock)获得时,根据它的兼容性锁本身是如何应对的. 一般来说,当执行UPDATE语句时,SQL Server会用到更新锁(Update Lock).如果你查看对应的执行计划,你会看到它包含3个部分 读取数据 计算新值 写入数据 在查询计划的第1部分

sql server 里的文件和文件组使用

转自:https://www.cnblogs.com/woodytu/p/5821827.html 在今天的文章里,我想谈下SQL Server里非常重要的话题:SQL Server如何处理文件的文件组.当你用CREATE DATABASE命令创建一个简单的数据库时,SQL Server为你创建2个文件: 一个数据文件(.mdf) 一个事务日志文件(.ldf) 数据文件本身在有且只有一个主文件组里创建.默认情况下,在主文件组里,SQL Server存储素有的数据(用户表,系统表等).那有额外的文

SQL Server里等待统计(Wait Statistics)介绍

在今天的文章里我想详细谈下SQL Server里的统计等待(Wait Statistics),还有她们如何帮助你立即为什么你的SQL Server当前很慢.一提到性能调优,对我来说统计等待是SQL Server了最重要的概念. 查询为什么等待 在SQL Server里每次你执行1个查询,查询总需要等待.什么?查询总需要等待?是的,你没有看错:但给你执行1个查询时,查询总需要等待.为什么查询需要等待的原因是SQL Server通过所谓的等待统计(Wait Statistics)来跟踪的.在我进入等

在SQL Server里我们为什么需要意向锁(Intent Locks)?

在1年前,我写了篇在SQL Server里为什么我们需要更新锁.今天我想继续这个讨论,谈下SQL Server里的意向锁,还有为什么需要它们. SQL Server里的锁层级 当我讨论SQL Server里的锁升级时,我从SQL Server使用的锁层级开始题,在你读或修改你的数据的时候. 当你读取一条记录时,SQL Server默认请求一个共享锁(S),当你修改一条记录时,SQL Server请求一个排它锁(X).这2个锁彼此不兼容,当你同时向读写一条记录时,会发生阻塞. 另外对于行级别的锁,

SQL Server里因丢失索引造成的死锁

原文:SQL Server里因丢失索引造成的死锁 在今天的文章里我想演示下SQL Server里在表上丢失索引如何引起死锁(deadlock)的.为了准备测试场景,下列代码会创建2个表,然后2个表都插入4条记录. 1 -- Create a table without any indexes 2 CREATE TABLE Table1 3 ( 4 Column1 INT, 5 Column2 INT 6 ) 7 GO 8 9 -- Insert a few record 10 INSERT IN

SQL Server里如何随机记录集

今天的文章,我想给你简单介绍下SQL Server里如何随机记录集. 1 SELECT * FROM Person.Person 2 ORDER BY NEWID() 3 GO 这会引入新的UNIQUEIDENTIFIER数据类型列,SQL Server会在那列上进行物理排序操作. 但是在记录集里列本身没有返回,因为ORDER BY子句在查询SELECT部分逻辑后发生,因此也不会改变记录集. 在SQL Server里,简单但很强大的方法用来随机化你的记录集. 感谢关注!

SQL Server Insert操作中的锁

原文:SQL Server Insert操作中的锁 这篇博文简单介绍一下在SQL Server中一条Insert语句中用到的锁. 准备数据 首先我们建立一张表Table_1,它有两列Id(bigint)和Value(varchar),其中Id建立了主键. CREATE TABLE [dbo].[Table_2]( [Id] [bigint] NOT NULL, [Value] [nchar](10) NULL, CONSTRAINT [PK_Table_2] PRIMARY KEY CLUSTE