天天看點

SQL Server裡的闩鎖耦合(Latch Coupling)索引查找操作(Index Seek Operations)闩鎖耦合實戰(Latch Coupling in Action)小結原文連結

幾年前,我寫了篇關于闩鎖和為什麼SQL Server需要它們的文章。在今天的文章裡,我想進一步談下非緩存區闩鎖(Non-Buffer Latches),還有在索引查找操作期間,SQL Server如何使用它們。在這裡你會學到稱為闩鎖耦合(Latch Coupling)的概念。

索引查找操作(Index Seek Operations)

正如你知道的,SQL Server使用掃描(Scan)和查找(Seek)操作在索引(聚集和非聚集索引)裡通路資料。這裡的查找操作使用B樹的導航結構在葉子節點查找特定的記錄。下圖展示了這個概念。

SQL Server裡的闩鎖耦合(Latch Coupling)索引查找操作(Index Seek Operations)闩鎖耦合實戰(Latch Coupling in Action)小結原文連結

在這個例子裡,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裡的闩鎖耦合(Latch Coupling)索引查找操作(Index Seek Operations)闩鎖耦合實戰(Latch Coupling in Action)小結原文連結

但更有意思的事是SQL Server獲得和釋放這些闩鎖的順序。一般你期望SQL Server在頁上獲得闩鎖,并最後釋放這個闩鎖。但事實并非如此!

我們來詳細看下。首先SQL 在索引根頁(975号頁)上獲得了一個共享闩鎖。在SQL Server處理那個頁後,聚集索引查找操作在接下來的層級裡,繼續讀取請求的頁,并在它上面擷取闩鎖(257号頁)。

注意在索引根頁上獲得的闩鎖還沒有釋放,它還保持擷取!

當在接下來的索引頁上成功擷取闩鎖後,在索引根頁上的闩鎖才會釋放。這個方法稱為闩鎖耦合(Latch Coupling)。這個必須的,因為SQL Server在B樹結構裡,跟随從一個頁到另一個頁的指針。

在頁處理期間,這個指針必須保持穩定。例如,在此期間不允許被另一個工作者線程(例如分頁操作)将此指針無效。是以SQL Server在(單線程)索引查找操作期間,同時把持2個闩鎖。下面這個圖檔很好的示範了這個重要概念。

SQL Server裡的闩鎖耦合(Latch Coupling)索引查找操作(Index Seek Operations)闩鎖耦合實戰(Latch Coupling in Action)小結原文連結

當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/